/***********************************************************************
*
* Copyright (c) 2012-2020 Barbara Geller
* Copyright (c) 2012-2020 Ansel Sermersheim
*
* Copyright (c) 2015 The Qt Company Ltd.
* Copyright (c) 2012-2016 Digia Plc and/or its subsidiary(-ies).
* Copyright (c) 2008-2012 Nokia Corporation and/or its subsidiary(-ies).
*
* This file is part of CopperSpice.
*
* CopperSpice is free software. You can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* CopperSpice is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* https://www.gnu.org/licenses/
*
***********************************************************************/

#include <qabstractitemview.h>

#ifndef QT_NO_ITEMVIEWS
#include <qpointer.h>
#include <qapplication.h>
#include <qclipboard.h>
#include <qpainter.h>
#include <qstyle.h>
#include <qdrag.h>
#include <qevent.h>
#include <qscrollbar.h>
#include <qwhatsthis.h>
#include <qtooltip.h>
#include <qdatetime.h>
#include <qlineedit.h>
#include <qspinbox.h>
#include <qtreeview.h>
#include <qtableview.h>
#include <qheaderview.h>
#include <qstyleditemdelegate.h>

#include <qabstractitemview_p.h>
#include <qabstractitemmodel_p.h>
#include <qguiapplication_p.h>

#ifndef QT_NO_ACCESSIBILITY
#include <qaccessible.h>
#endif

#ifndef QT_NO_GESTURES
#include <qscroller.h>
#endif


QAbstractItemViewPrivate::QAbstractItemViewPrivate()
   :   model(QAbstractItemModelPrivate::staticEmptyModel()),
       itemDelegate(0),
       selectionModel(0),
       ctrlDragSelectionFlag(QItemSelectionModel::NoUpdate),
       noSelectionOnMousePress(false),
       selectionMode(QAbstractItemView::ExtendedSelection),
       selectionBehavior(QAbstractItemView::SelectItems),
       currentlyCommittingEditor(0),
       pressedModifiers(Qt::NoModifier),
       pressedPosition(QPoint(-1, -1)),
       pressedAlreadySelected(false),
       viewportEnteredNeeded(false),
       state(QAbstractItemView::NoState),
       stateBeforeAnimation(QAbstractItemView::NoState),
       editTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed),
       lastTrigger(QAbstractItemView::NoEditTriggers),
       tabKeyNavigation(false),

#ifndef QT_NO_DRAGANDDROP
       showDropIndicator(true),
       dragEnabled(false),
       dragDropMode(QAbstractItemView::NoDragDrop),
       overwrite(false),
       dropIndicatorPosition(QAbstractItemView::OnItem),
       defaultDropAction(Qt::IgnoreAction),
#endif

       autoScroll(true),
       autoScrollMargin(16),
       autoScrollCount(0),
       shouldScrollToCurrentOnShow(false),
       shouldClearStatusTip(false),
       alternatingColors(false),
       textElideMode(Qt::ElideRight),
       verticalScrollMode(QAbstractItemView::ScrollPerItem),
       horizontalScrollMode(QAbstractItemView::ScrollPerItem),
       currentIndexSet(false),
       wrapItemText(false),
       delayedPendingLayout(true),
       moveCursorUpdatedView(false)
{
   keyboardInputTime.invalidate();
}

QAbstractItemViewPrivate::~QAbstractItemViewPrivate()
{
}

void QAbstractItemViewPrivate::init()
{
   Q_Q(QAbstractItemView);

   q->setItemDelegate(new QStyledItemDelegate(q));

   vbar->setRange(0, 0);
   hbar->setRange(0, 0);

   QObject::connect(vbar, &QScrollBar::actionTriggered,  q, &QAbstractItemView::verticalScrollbarAction);
   QObject::connect(hbar, &QScrollBar::actionTriggered,  q, &QAbstractItemView::horizontalScrollbarAction);
   QObject::connect(vbar, &QScrollBar::valueChanged,     q, &QAbstractItemView::verticalScrollbarValueChanged);
   QObject::connect(hbar, &QScrollBar::valueChanged,     q, &QAbstractItemView::horizontalScrollbarValueChanged);

   viewport->setBackgroundRole(QPalette::Base);

   q->setAttribute(Qt::WA_InputMethodEnabled);
}

void QAbstractItemViewPrivate::setHoverIndex(const QPersistentModelIndex &index)
{
   Q_Q(QAbstractItemView);

   if (hover == index) {
      return;
   }

   if (selectionBehavior != QAbstractItemView::SelectRows) {
      q->update(hover); //update the old one
      q->update(index); //update the new one
   } else {
      QRect oldHoverRect = q->visualRect(hover);
      QRect newHoverRect = q->visualRect(index);
      viewport->update(QRect(0, newHoverRect.y(), viewport->width(), newHoverRect.height()));
      viewport->update(QRect(0, oldHoverRect.y(), viewport->width(), oldHoverRect.height()));
   }
   hover = index;
}

void QAbstractItemViewPrivate::checkMouseMove(const QPersistentModelIndex &index)
{
   //we take a persistent model index because the model might change by emitting signals
   Q_Q(QAbstractItemView);

   setHoverIndex(index);
   if (viewportEnteredNeeded || enteredIndex != index) {
      viewportEnteredNeeded = false;

      if (index.isValid()) {
         emit q->entered(index);

#ifndef QT_NO_STATUSTIP
         QString statustip = model->data(index, Qt::StatusTipRole).toString();
         if (q->parent() && (shouldClearStatusTip || !statustip.isEmpty())) {
            QStatusTipEvent tip(statustip);
            QApplication::sendEvent(q->parent(), &tip);
            shouldClearStatusTip = !statustip.isEmpty();
         }
#endif
      } else {
#ifndef QT_NO_STATUSTIP
         if (q->parent() && shouldClearStatusTip) {
            QString emptyString;
            QStatusTipEvent tip( emptyString );
            QApplication::sendEvent(q->parent(), &tip);
         }
#endif
         emit q->viewportEntered();
      }
      enteredIndex = index;
   }
}

#ifndef QT_NO_GESTURES

// stores and restores the selection and current item when flicking
void QAbstractItemViewPrivate::_q_scrollerStateChanged()
{
   Q_Q(QAbstractItemView);

   if (QScroller *scroller = QScroller::scroller(viewport)) {
      switch (scroller->state()) {
         case QScroller::Pressed:
            // store the current selection in case we start scrolling
            if (q->selectionModel()) {
               oldSelection = q->selectionModel()->selection();
               oldCurrent = q->selectionModel()->currentIndex();
            }
            break;

         case QScroller::Dragging:
            // restore the old selection if we really start scrolling
            if (q->selectionModel()) {
               q->selectionModel()->select(oldSelection, QItemSelectionModel::ClearAndSelect);
               q->selectionModel()->setCurrentIndex(oldCurrent, QItemSelectionModel::NoUpdate);
            }
            [[fallthrough]];

         default:
            oldSelection = QItemSelection();
            oldCurrent = QModelIndex();
            break;
      }
   }
}

#endif // QT_NO_GESTURES

QAbstractItemView::QAbstractItemView(QWidget *parent)
   : QAbstractScrollArea(*(new QAbstractItemViewPrivate), parent)
{
   d_func()->init();
}

// internal
QAbstractItemView::QAbstractItemView(QAbstractItemViewPrivate &dd, QWidget *parent)
   : QAbstractScrollArea(dd, parent)
{
   d_func()->init();
}

QAbstractItemView::~QAbstractItemView()
{
   Q_D(QAbstractItemView);

   // stop these timers here before ~QObject
   d->delayedReset.stop();
   d->updateTimer.stop();
   d->delayedEditing.stop();
   d->delayedAutoScroll.stop();
   d->autoScrollTimer.stop();
   d->delayedLayout.stop();
   d->fetchMoreTimer.stop();
}

void QAbstractItemView::setModel(QAbstractItemModel *model)
{
   Q_D(QAbstractItemView);

   if (model == d->model) {
      return;
   }

   if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
      disconnect(d->model, &QAbstractItemModel::destroyed,               this, &QAbstractItemView::_q_modelDestroyed);
      disconnect(d->model, &QAbstractItemModel::dataChanged,             this, &QAbstractItemView::dataChanged);

      disconnect(d->model, &QAbstractItemModel::headerDataChanged,       this, &QAbstractItemView::_q_headerDataChanged);
      disconnect(d->model, &QAbstractItemModel::rowsInserted,            this, &QAbstractItemView::rowsInserted);
      disconnect(d->model, &QAbstractItemModel::rowsInserted,            this, &QAbstractItemView::_q_rowsInserted);
      disconnect(d->model, &QAbstractItemModel::rowsAboutToBeRemoved,    this, &QAbstractItemView::rowsAboutToBeRemoved);
      disconnect(d->model, &QAbstractItemModel::rowsRemoved,             this, &QAbstractItemView::_q_rowsRemoved);
      disconnect(d->model, &QAbstractItemModel::rowsMoved,               this, &QAbstractItemView::_q_rowsMoved);

      disconnect(d->model, &QAbstractItemModel::columnsAboutToBeRemoved, this, &QAbstractItemView::_q_columnsAboutToBeRemoved);
      disconnect(d->model, &QAbstractItemModel::columnsRemoved,          this, &QAbstractItemView::_q_columnsRemoved);
      disconnect(d->model, &QAbstractItemModel::columnsInserted,         this, &QAbstractItemView::_q_columnsInserted);
      disconnect(d->model, &QAbstractItemModel::columnsMoved,            this, &QAbstractItemView::_q_columnsMoved);
      disconnect(d->model, &QAbstractItemModel::modelReset,              this, &QAbstractItemView::reset);
      disconnect(d->model, &QAbstractItemModel::layoutChanged,           this, &QAbstractItemView::_q_layoutChanged);
   }

   d->model = (model ? model : QAbstractItemModelPrivate::staticEmptyModel());

   // sserts do basic sanity checking of the model
   Q_ASSERT_X(d->model->index(0, 0) == d->model->index(0, 0), "QAbstractItemView::setModel():",
      "Model should return the exact same index (including its internal id/pointer) when asked for it twice in a row.");

   Q_ASSERT_X(! d->model->index(0, 0).parent().isValid(),
      "QAbstractItemView::setModel():", "Parent of a top level index should be invalid");

   if (d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
      connect(d->model, &QAbstractItemModel::destroyed,               this, &QAbstractItemView::_q_modelDestroyed);
      connect(d->model, &QAbstractItemModel::dataChanged,             this, &QAbstractItemView::dataChanged);

      connect(d->model, &QAbstractItemModel::headerDataChanged,       this, &QAbstractItemView::_q_headerDataChanged);
      connect(d->model, &QAbstractItemModel::rowsInserted,            this, &QAbstractItemView::rowsInserted);
      connect(d->model, &QAbstractItemModel::rowsInserted,            this, &QAbstractItemView::_q_rowsInserted);
      connect(d->model, &QAbstractItemModel::rowsAboutToBeRemoved,    this, &QAbstractItemView::rowsAboutToBeRemoved);
      connect(d->model, &QAbstractItemModel::rowsRemoved,             this, &QAbstractItemView::_q_rowsRemoved);
      connect(d->model, &QAbstractItemModel::rowsMoved,               this, &QAbstractItemView::_q_rowsMoved);

      connect(d->model, &QAbstractItemModel::columnsAboutToBeRemoved, this, &QAbstractItemView::_q_columnsAboutToBeRemoved);
      connect(d->model, &QAbstractItemModel::columnsRemoved,          this, &QAbstractItemView::_q_columnsRemoved);
      connect(d->model, &QAbstractItemModel::columnsInserted,         this, &QAbstractItemView::_q_columnsInserted);
      connect(d->model, &QAbstractItemModel::columnsMoved,            this, &QAbstractItemView::_q_columnsMoved);
      connect(d->model, &QAbstractItemModel::modelReset,              this, &QAbstractItemView::reset);
      connect(d->model, &QAbstractItemModel::layoutChanged,           this, &QAbstractItemView::_q_layoutChanged);
   }

   QItemSelectionModel *selection_model = new QItemSelectionModel(d->model, this);
   connect(d->model, &QAbstractItemModel::destroyed, selection_model, &QItemSelectionModel::deleteLater);
   setSelectionModel(selection_model);

   reset(); // kill editors, set new root and do layout
}

QAbstractItemModel *QAbstractItemView::model() const
{
   Q_D(const QAbstractItemView);
   return (d->model == QAbstractItemModelPrivate::staticEmptyModel() ? 0 : d->model);
}

void QAbstractItemView::setSelectionModel(QItemSelectionModel *selectionModel)
{
   // ### if the given model is null, we should use the original selection model
   Q_ASSERT(selectionModel);

   Q_D(QAbstractItemView);

   if (selectionModel->model() != d->model) {
      qWarning("QAbstractItemView::setSelectionModel() failed: "
         "Trying to set a selection model, which works on a different model than the view.");
      return;
   }

   QItemSelection oldSelection;
   QModelIndex oldCurrentIndex;

   if (d->selectionModel) {
      if (d->selectionModel->model() == selectionModel->model()) {
         oldSelection = d->selectionModel->selection();
         oldCurrentIndex = d->selectionModel->currentIndex();
      }

      disconnect(d->selectionModel.data(), &QItemSelectionModel::selectionChanged, this, &QAbstractItemView::selectionChanged);
      disconnect(d->selectionModel.data(), &QItemSelectionModel::currentChanged,   this, &QAbstractItemView::currentChanged);
   }

   d->selectionModel = selectionModel;

   if (d->selectionModel) {

      connect(d->selectionModel.data(), &QItemSelectionModel::selectionChanged, this, &QAbstractItemView::selectionChanged);
      connect(d->selectionModel.data(), &QItemSelectionModel::currentChanged,   this, &QAbstractItemView::currentChanged);

      selectionChanged(d->selectionModel->selection(), oldSelection);
      currentChanged(d->selectionModel->currentIndex(), oldCurrentIndex);
   }
}

QItemSelectionModel *QAbstractItemView::selectionModel() const
{
   Q_D(const QAbstractItemView);
   return d->selectionModel;
}

void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate)
{
   Q_D(QAbstractItemView);

   if (delegate == d->itemDelegate) {
      return;
   }

   if (d->itemDelegate) {
      if (d->delegateRefCount(d->itemDelegate) == 1) {
         disconnect(d->itemDelegate.data(), &QAbstractItemDelegate::closeEditor,     this, &QAbstractItemView::closeEditor);
         disconnect(d->itemDelegate.data(), &QAbstractItemDelegate::commitData,      this, &QAbstractItemView::commitData);
         disconnect(d->itemDelegate.data(), &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemView::doItemsLayout);
      }
   }

   if (delegate) {
      if (d->delegateRefCount(delegate) == 0) {
         connect(delegate, &QAbstractItemDelegate::closeEditor,     this, &QAbstractItemView::closeEditor);
         connect(delegate, &QAbstractItemDelegate::commitData,      this, &QAbstractItemView::commitData);
         connect(delegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemView::doItemsLayout, Qt::QueuedConnection);
      }
   }

   d->itemDelegate = delegate;
   viewport()->update();

   d->doDelayedItemsLayout();
}

QAbstractItemDelegate *QAbstractItemView::itemDelegate() const
{
   return d_func()->itemDelegate;
}

QVariant QAbstractItemView::inputMethodQuery(Qt::InputMethodQuery query) const
{
   const QModelIndex current = currentIndex();

   if (! current.isValid() || query != Qt::ImCursorRectangle) {
      return QAbstractScrollArea::inputMethodQuery(query);
   }

   return visualRect(current);
}

void QAbstractItemView::setItemDelegateForRow(int row, QAbstractItemDelegate *delegate)
{
   Q_D(QAbstractItemView);

   if (QAbstractItemDelegate *rowDelegate = d->rowDelegates.value(row, 0)) {

      if (d->delegateRefCount(rowDelegate) == 1) {
         disconnect(rowDelegate, &QAbstractItemDelegate::closeEditor,     this, &QAbstractItemView::closeEditor);
         disconnect(rowDelegate, &QAbstractItemDelegate::commitData,      this, &QAbstractItemView::commitData);
         disconnect(rowDelegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemView::doItemsLayout);
      }
      d->rowDelegates.remove(row);
   }

   if (delegate) {
      if (d->delegateRefCount(delegate) == 0) {
         connect(delegate, &QAbstractItemDelegate::closeEditor,     this, &QAbstractItemView::closeEditor);
         connect(delegate, &QAbstractItemDelegate::commitData,      this, &QAbstractItemView::commitData);
         connect(delegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemView::doItemsLayout, Qt::QueuedConnection);
      }
      d->rowDelegates.insert(row, delegate);
   }

   viewport()->update();
   d->doDelayedItemsLayout();
}

QAbstractItemDelegate *QAbstractItemView::itemDelegateForRow(int row) const
{
   Q_D(const QAbstractItemView);
   return d->rowDelegates.value(row, 0);
}

void QAbstractItemView::setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate)
{
   Q_D(QAbstractItemView);

   if (QAbstractItemDelegate *columnDelegate = d->columnDelegates.value(column, 0)) {
      if (d->delegateRefCount(columnDelegate) == 1) {
         disconnect(columnDelegate, &QAbstractItemDelegate::closeEditor,     this, &QAbstractItemView::closeEditor);
         disconnect(columnDelegate, &QAbstractItemDelegate::commitData,      this, &QAbstractItemView::commitData);
         disconnect(columnDelegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemView::doItemsLayout);
      }
      d->columnDelegates.remove(column);
   }

   if (delegate) {
      if (d->delegateRefCount(delegate) == 0) {
         connect(delegate, &QAbstractItemDelegate::closeEditor,     this, &QAbstractItemView::closeEditor);
         connect(delegate, &QAbstractItemDelegate::commitData,      this, &QAbstractItemView::commitData);
         connect(delegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemView::doItemsLayout, Qt::QueuedConnection);
      }
      d->columnDelegates.insert(column, delegate);
   }

   viewport()->update();
   d->doDelayedItemsLayout();
}

QAbstractItemDelegate *QAbstractItemView::itemDelegateForColumn(int column) const
{
   Q_D(const QAbstractItemView);
   return d->columnDelegates.value(column, 0);
}

QAbstractItemDelegate *QAbstractItemView::itemDelegate(const QModelIndex &index) const
{
   Q_D(const QAbstractItemView);
   return d->delegateForIndex(index);
}

void QAbstractItemView::setSelectionMode(SelectionMode mode)
{
   Q_D(QAbstractItemView);
   d->selectionMode = mode;
}

QAbstractItemView::SelectionMode QAbstractItemView::selectionMode() const
{
   Q_D(const QAbstractItemView);
   return d->selectionMode;
}

void QAbstractItemView::setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)
{
   Q_D(QAbstractItemView);
   d->selectionBehavior = behavior;
}

QAbstractItemView::SelectionBehavior QAbstractItemView::selectionBehavior() const
{
   Q_D(const QAbstractItemView);
   return d->selectionBehavior;
}

void QAbstractItemView::setCurrentIndex(const QModelIndex &index)
{
   Q_D(QAbstractItemView);
   if (d->selectionModel && (!index.isValid() || d->isIndexEnabled(index))) {
      QItemSelectionModel::SelectionFlags command = selectionCommand(index, 0);
      d->selectionModel->setCurrentIndex(index, command);
      d->currentIndexSet = true;

      if ((command & QItemSelectionModel::Current) == 0) {
         d->currentSelectionStartIndex = index;
      }
   }
}

QModelIndex QAbstractItemView::currentIndex() const
{
   Q_D(const QAbstractItemView);
   return d->selectionModel ? d->selectionModel->currentIndex() : QModelIndex();
}

void QAbstractItemView::reset()
{
   Q_D(QAbstractItemView);

   d->delayedReset.stop(); //make sure we stop the timer

   for (const QEditorInfo &info : d->indexEditorHash) {
      if (info.widget) {
         d->releaseEditor(info.widget.data(), d->indexForEditor(info.widget.data()));
      }
   }

   d->editorIndexHash.clear();
   d->indexEditorHash.clear();
   d->persistent.clear();
   d->currentIndexSet = false;

   setState(NoState);
   setRootIndex(QModelIndex());

   if (d->selectionModel) {
      d->selectionModel->reset();
   }
#ifndef QT_NO_ACCESSIBILITY

   if (QAccessible::isActive()) {
      QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::ModelReset);
      QAccessible::updateAccessibility(&accessibleEvent);
   }
#endif

   d->updateGeometry();
}

void QAbstractItemView::setRootIndex(const QModelIndex &index)
{
   Q_D(QAbstractItemView);
   if (index.isValid() && index.model() != d->model) {
      qWarning("QAbstractItemView::setRootIndex failed : index must be from the currently set model");
      return;
   }
   d->root = index;
   d->doDelayedItemsLayout();
   d->updateGeometry();
}

QModelIndex QAbstractItemView::rootIndex() const
{
   return QModelIndex(d_func()->root);
}

void QAbstractItemView::selectAll()
{
   Q_D(QAbstractItemView);

   SelectionMode mode = d->selectionMode;

   if (mode == MultiSelection || mode == ExtendedSelection) {
      d->selectAll(QItemSelectionModel::ClearAndSelect | d->selectionBehaviorFlags());


   } else if (mode != SingleSelection) {
      d->selectAll(selectionCommand(d->model->index(0, 0, d->root)));
   }
}

void QAbstractItemView::edit(const QModelIndex &index)
{
   Q_D(QAbstractItemView);

   if (! d->isIndexValid(index)) {
      qWarning("edit() Index was invalid");
   }

   if (! edit(index, AllEditTriggers, 0)) {
      qWarning("edit() Editing failed");
   }
}

void QAbstractItemView::clearSelection()
{
   Q_D(QAbstractItemView);

   if (d->selectionModel) {
      d->selectionModel->clearSelection();
   }
}

void QAbstractItemView::doItemsLayout()
{
   Q_D(QAbstractItemView);

   d->interruptDelayedItemsLayout();
   updateGeometries();
   d->viewport->update();
}

void QAbstractItemView::setEditTriggers(EditTriggers actions)
{
   Q_D(QAbstractItemView);
   d->editTriggers = actions;
}

QAbstractItemView::EditTriggers QAbstractItemView::editTriggers() const
{
   Q_D(const QAbstractItemView);
   return d->editTriggers;
}

void QAbstractItemView::setVerticalScrollMode(ScrollMode mode)
{
   Q_D(QAbstractItemView);

   if (mode == d->verticalScrollMode) {
      return;
   }

   QModelIndex topLeft = indexAt(QPoint(0, 0));
   d->verticalScrollMode = mode;
   updateGeometries(); // update the scroll bars

   scrollTo(topLeft, QAbstractItemView::PositionAtTop);
}

QAbstractItemView::ScrollMode QAbstractItemView::verticalScrollMode() const
{
   Q_D(const QAbstractItemView);
   return d->verticalScrollMode;
}

void QAbstractItemView::setHorizontalScrollMode(ScrollMode mode)
{
   Q_D(QAbstractItemView);
   d->horizontalScrollMode = mode;
   updateGeometries(); // update the scroll bars
}

QAbstractItemView::ScrollMode QAbstractItemView::horizontalScrollMode() const
{
   Q_D(const QAbstractItemView);
   return d->horizontalScrollMode;
}

#ifndef QT_NO_DRAGANDDROP

void QAbstractItemView::setDragDropOverwriteMode(bool overwrite)
{
   Q_D(QAbstractItemView);
   d->overwrite = overwrite;
}

bool QAbstractItemView::dragDropOverwriteMode() const
{
   Q_D(const QAbstractItemView);
   return d->overwrite;
}
#endif

void QAbstractItemView::setAutoScroll(bool enable)
{
   Q_D(QAbstractItemView);
   d->autoScroll = enable;
}

bool QAbstractItemView::hasAutoScroll() const
{
   Q_D(const QAbstractItemView);
   return d->autoScroll;
}

void QAbstractItemView::setAutoScrollMargin(int margin)
{
   Q_D(QAbstractItemView);
   d->autoScrollMargin = margin;
}

int QAbstractItemView::autoScrollMargin() const
{
   Q_D(const QAbstractItemView);
   return d->autoScrollMargin;
}

void QAbstractItemView::setTabKeyNavigation(bool enable)
{
   Q_D(QAbstractItemView);
   d->tabKeyNavigation = enable;
}

bool QAbstractItemView::tabKeyNavigation() const
{
   Q_D(const QAbstractItemView);
   return d->tabKeyNavigation;
}

QSize QAbstractItemView::viewportSizeHint() const
{
   return QAbstractScrollArea::viewportSizeHint();
}

#ifndef QT_NO_DRAGANDDROP

void QAbstractItemView::setDropIndicatorShown(bool enable)
{
   Q_D(QAbstractItemView);
   d->showDropIndicator = enable;
}

bool QAbstractItemView::showDropIndicator() const
{
   Q_D(const QAbstractItemView);
   return d->showDropIndicator;
}

void QAbstractItemView::setDragEnabled(bool enable)
{
   Q_D(QAbstractItemView);
   d->dragEnabled = enable;
}

bool QAbstractItemView::dragEnabled() const
{
   Q_D(const QAbstractItemView);
   return d->dragEnabled;
}

void QAbstractItemView::setDragDropMode(DragDropMode behavior)
{
   Q_D(QAbstractItemView);
   d->dragDropMode = behavior;
   setDragEnabled(behavior == DragOnly || behavior == DragDrop || behavior == InternalMove);
   setAcceptDrops(behavior == DropOnly || behavior == DragDrop || behavior == InternalMove);
}

QAbstractItemView::DragDropMode QAbstractItemView::dragDropMode() const
{
   Q_D(const QAbstractItemView);
   DragDropMode setBehavior = d->dragDropMode;
   if (!dragEnabled() && !acceptDrops()) {
      return NoDragDrop;
   }

   if (dragEnabled() && !acceptDrops()) {
      return DragOnly;
   }

   if (!dragEnabled() && acceptDrops()) {
      return DropOnly;
   }

   if (dragEnabled() && acceptDrops()) {
      if (setBehavior == InternalMove) {
         return setBehavior;
      } else {
         return DragDrop;
      }
   }

   return NoDragDrop;
}

void QAbstractItemView::setDefaultDropAction(Qt::DropAction dropAction)
{
   Q_D(QAbstractItemView);
   d->defaultDropAction = dropAction;
}

Qt::DropAction QAbstractItemView::defaultDropAction() const
{
   Q_D(const QAbstractItemView);
   return d->defaultDropAction;
}

#endif // QT_NO_DRAGANDDROP

void QAbstractItemView::setAlternatingRowColors(bool enable)
{
   Q_D(QAbstractItemView);
   d->alternatingColors = enable;
   if (isVisible()) {
      d->viewport->update();
   }
}

bool QAbstractItemView::alternatingRowColors() const
{
   Q_D(const QAbstractItemView);
   return d->alternatingColors;
}

void QAbstractItemView::setIconSize(const QSize &size)
{
   Q_D(QAbstractItemView);

   if (size == d->iconSize) {
      return;
   }

   d->iconSize = size;
   d->doDelayedItemsLayout();

   emit iconSizeChanged(size);
}

QSize QAbstractItemView::iconSize() const
{
   Q_D(const QAbstractItemView);
   return d->iconSize;
}

void QAbstractItemView::setTextElideMode(Qt::TextElideMode mode)
{
   Q_D(QAbstractItemView);
   d->textElideMode = mode;
}

Qt::TextElideMode QAbstractItemView::textElideMode() const
{
   return d_func()->textElideMode;
}

/*!
  \reimp
*/
bool QAbstractItemView::focusNextPrevChild(bool next)
{
   Q_D(QAbstractItemView);
   if (d->tabKeyNavigation && isEnabled() && d->viewport->isEnabled()) {
      QKeyEvent event(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
      keyPressEvent(&event);
      if (event.isAccepted()) {
         return true;
      }
   }
   return QAbstractScrollArea::focusNextPrevChild(next);
}

/*!
  \reimp
*/
bool QAbstractItemView::event(QEvent *event)
{
   Q_D(QAbstractItemView);
   switch (event->type()) {

      case QEvent::Paint:
         //we call this here because the scrollbars' visibility might be altered
         //so this can't be done in the paintEvent method
         d->executePostedLayout(); //make sure we set the layout properly
         break;

      case QEvent::Show:
         d->executePostedLayout();    //make sure we set the layout properly

         if (d->shouldScrollToCurrentOnShow) {
            d->shouldScrollToCurrentOnShow = false;
            const QModelIndex current = currentIndex();

            if (current.isValid() && (d->state == QAbstractItemView::EditingState || d->autoScroll)) {
               scrollTo(current);
            }
         }
         break;

      case QEvent::LocaleChange:
         viewport()->update();
         break;

      case QEvent::LayoutDirectionChange:
      case QEvent::ApplicationLayoutDirectionChange:
         updateGeometries();
         break;

      case QEvent::StyleChange:
         doItemsLayout();
         break;

      case QEvent::FocusOut:
         d->checkPersistentEditorFocus();
         break;
      case QEvent::FontChange:
         d->doDelayedItemsLayout(); // the size of the items will change
         break;

      default:
         break;
   }
   return QAbstractScrollArea::event(event);
}

bool QAbstractItemView::viewportEvent(QEvent *event)
{
   Q_D(QAbstractItemView);

   switch (event->type()) {

      case QEvent::HoverMove:
      case QEvent::HoverEnter:
         d->setHoverIndex(indexAt(static_cast<QHoverEvent *>(event)->pos()));
         break;

      case QEvent::HoverLeave:
         d->setHoverIndex(QModelIndex());
         break;

      case QEvent::Enter:
         d->viewportEnteredNeeded = true;
         break;

      case QEvent::Leave:
#ifndef QT_NO_STATUSTIP
         if (d->shouldClearStatusTip && this->parent()) {
            QString empty;
            QStatusTipEvent tip(empty);
            QApplication::sendEvent(this->parent(), &tip);
            d->shouldClearStatusTip = false;
         }
#endif
         d->enteredIndex = QModelIndex();
         break;

      case QEvent::ToolTip:
      case QEvent::QueryWhatsThis:
      case QEvent::WhatsThis: {
         QHelpEvent *he = static_cast<QHelpEvent *>(event);
         const QModelIndex index = indexAt(he->pos());

         QStyleOptionViewItem option = d->viewOptions();
         option.rect = visualRect(index);
         option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);


         QAbstractItemDelegate *delegate = d->delegateForIndex(index);
         if (!delegate) {
            return false;
         }
         return delegate->helpEvent(he, this, option, index);
      }


      case QEvent::FontChange:
         d->doDelayedItemsLayout();    // the size of the items will change
         break;

      case QEvent::WindowActivate:
      case QEvent::WindowDeactivate:
         d->viewport->update();
         break;

      case QEvent::ScrollPrepare:
         executeDelayedItemsLayout();

#ifndef QT_NO_GESTURES
         connect(QScroller::scroller(d->viewport), &QScroller::stateChanged, this,
                  &QAbstractItemView::_q_scrollerStateChanged, Qt::UniqueConnection);
#endif
         break;

      default:
         break;
   }

   return QAbstractScrollArea::viewportEvent(event);
}

void QAbstractItemView::mousePressEvent(QMouseEvent *event)
{
   Q_D(QAbstractItemView);

   d->delayedAutoScroll.stop();                   // any interaction with the view cancel the auto scrolling
   QPoint pos = event->pos();
   QPersistentModelIndex index = indexAt(pos);

   if (! d->selectionModel || (d->state == EditingState && d->hasEditor(index))) {
      return;
   }

   d->pressedAlreadySelected = d->selectionModel->isSelected(index);
   d->pressedIndex = index;
   d->pressedModifiers = event->modifiers();

   QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
   d->noSelectionOnMousePress = command == QItemSelectionModel::NoUpdate || ! index.isValid();

   QPoint offset = d->offset();
   if ((command & QItemSelectionModel::Current) == 0) {
      d->pressedPosition = pos + offset;
      d->currentSelectionStartIndex = index;

   } else if (!d->currentSelectionStartIndex.isValid()) {
      d->currentSelectionStartIndex = currentIndex();
   }

   if (edit(index, NoEditTriggers, event)) {
      return;
   }

   if (index.isValid() && d->isIndexEnabled(index)) {
      // we disable scrollTo for mouse press so the item doesn't change position
      // when the user is interacting with it (ie. clicking on it)

      bool autoScroll = d->autoScroll;
      d->autoScroll = false;
      d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
      d->autoScroll = autoScroll;

      if (command.testFlag(QItemSelectionModel::Toggle)) {
         command &= ~QItemSelectionModel::Toggle;
         d->ctrlDragSelectionFlag = d->selectionModel->isSelected(index) ? QItemSelectionModel::Deselect :
            QItemSelectionModel::Select;
         command |= d->ctrlDragSelectionFlag;
      }
      if ((command & QItemSelectionModel::Current) == 0) {
         setSelection(QRect(pos, QSize(1, 1)), command);
      } else {
         QRect rect(visualRect(d->currentSelectionStartIndex).center(), pos);
         setSelection(rect, command);
      }

      // signal handlers may change the model
      emit pressed(index);
      if (d->autoScroll) {
         //we delay the autoscrolling to filter out double click event
         //100 is to be sure that there won't be a double-click misinterpreted as a 2 single clicks
         d->delayedAutoScroll.start(QApplication::doubleClickInterval() + 100, this);
      }

   } else {
      // Forces a finalize() even if mouse is pressed, but not on a item
      d->selectionModel->select(QModelIndex(), QItemSelectionModel::Select);
   }
}

void QAbstractItemView::mouseMoveEvent(QMouseEvent *event)
{
   Q_D(QAbstractItemView);

   QPoint topLeft;
   QPoint bottomRight = event->pos();

   if (state() == ExpandingState || state() == CollapsingState) {
      return;
   }

#ifndef QT_NO_DRAGANDDROP
   if (state() == DraggingState) {
      topLeft = d->pressedPosition - d->offset();
      if ((topLeft - bottomRight).manhattanLength() > QApplication::startDragDistance()) {
         d->pressedIndex = QModelIndex();
         startDrag(d->model->supportedDragActions());
         setState(NoState); // the startDrag will return when the dnd operation is done
         stopAutoScroll();
      }
      return;
   }
#endif // QT_NO_DRAGANDDROP

   QPersistentModelIndex index = indexAt(bottomRight);
   QModelIndex buddy = d->model->buddy(d->pressedIndex);
   if ((state() == EditingState && d->hasEditor(buddy))
      || edit(index, NoEditTriggers, event)) {
      return;
   }

   if (d->selectionMode != SingleSelection) {
      topLeft = d->pressedPosition - d->offset();
   } else {
      topLeft = bottomRight;
   }

   d->checkMouseMove(index);

#ifndef QT_NO_DRAGANDDROP
   if (d->pressedIndex.isValid()
      && d->dragEnabled
      && (state() != DragSelectingState)
      && (event->buttons() != Qt::NoButton)
      && !d->selectedDraggableIndexes().isEmpty()) {
      setState(DraggingState);
      return;
   }
#endif

   if ((event->buttons() & Qt::LeftButton) && d->selectionAllowed(index) && d->selectionModel) {
      setState(DragSelectingState);
      QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
      if (d->ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && command.testFlag(QItemSelectionModel::Toggle)) {
         command &= ~QItemSelectionModel::Toggle;
         command |= d->ctrlDragSelectionFlag;
      }

      // Do the normalize ourselves, since QRect::normalized() is flawed
      QRect selectionRect = QRect(topLeft, bottomRight);
      setSelection(selectionRect, command);

      // set at the end because it might scroll the view
      if (index.isValid()
         && (index != d->selectionModel->currentIndex())
         && d->isIndexEnabled(index)) {
         d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
      }
   }
}

/*!
    This function is called with the given \a event when a mouse button is released,
    after a mouse press event on the widget. If a user presses the mouse inside your
    widget and then drags the mouse to another location before releasing the mouse button,
    your widget receives the release event. The function will emit the clicked() signal if an
    item was being pressed.
*/
void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event)
{
   Q_D(QAbstractItemView);

   QPoint pos = event->pos();
   QPersistentModelIndex index = indexAt(pos);

   if (state() == EditingState) {
      if (d->isIndexValid(index)
         && d->isIndexEnabled(index)
         && d->sendDelegateEvent(index, event)) {
         update(index);
      }
      return;
   }

   bool click = (index == d->pressedIndex && index.isValid());
   bool selectedClicked = click && (event->button() == Qt::LeftButton) && d->pressedAlreadySelected;
   EditTrigger trigger = (selectedClicked ? SelectedClicked : NoEditTriggers);
   bool edited = edit(index, trigger, event);

   d->ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate;

   if (d->selectionModel && d->noSelectionOnMousePress) {
      d->noSelectionOnMousePress = false;
      d->selectionModel->select(index, selectionCommand(index, event));
   }

   setState(NoState);

   if (click) {
      if (event->button() == Qt::LeftButton) {
         emit clicked(index);
      }

      if (edited) {
         return;
      }

      QStyleOptionViewItem option = d->viewOptions();

      if (d->pressedAlreadySelected) {
         option.state |= QStyle::State_Selected;
      }

      if ((model()->flags(index) & Qt::ItemIsEnabled)
         && style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) {
         emit activated(index);
      }
   }
}

/*!
    This function is called with the given \a event when a mouse button is
    double clicked inside the widget. If the double-click is on a valid item it
    emits the doubleClicked() signal and calls edit() on the item.
*/
void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event)
{
   Q_D(QAbstractItemView);

   QModelIndex index = indexAt(event->pos());
   if (!index.isValid()
      || !d->isIndexEnabled(index)
      || (d->pressedIndex != index)) {
      QMouseEvent me(QEvent::MouseButtonPress,
         event->localPos(), event->windowPos(), event->screenPos(),
         event->button(), event->buttons(), event->modifiers(), event->source());
      mousePressEvent(&me);
      return;
   }
   // signal handlers may change the model
   QPersistentModelIndex persistent = index;
   emit doubleClicked(persistent);
   if ((event->button() == Qt::LeftButton) && !edit(persistent, DoubleClicked, event)
      && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this)) {
      emit activated(persistent);
   }
}

#ifndef QT_NO_DRAGANDDROP

/*!
    This function is called with the given \a event when a drag and drop operation enters
    the widget. If the drag is over a valid dropping place (e.g. over an item that
    accepts drops), the event is accepted; otherwise it is ignored.

    \sa dropEvent() startDrag()
*/
void QAbstractItemView::dragEnterEvent(QDragEnterEvent *event)
{
   if (dragDropMode() == InternalMove
      && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) {
      return;
   }

   if (d_func()->canDrop(event)) {
      event->accept();
      setState(DraggingState);
   } else {
      event->ignore();
   }
}

void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event)
{
   Q_D(QAbstractItemView);
   if (dragDropMode() == InternalMove
      && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) {
      return;
   }

   // ignore by default
   event->ignore();

   QModelIndex index = indexAt(event->pos());
   d->hover = index;
   if (!d->droppingOnItself(event, index)
      && d->canDrop(event)) {

      if (index.isValid() && d->showDropIndicator) {
         QRect rect = visualRect(index);
         d->dropIndicatorPosition = d->position(event->pos(), rect, index);
         switch (d->dropIndicatorPosition) {
            case AboveItem:
               if (d->isIndexDropEnabled(index.parent())) {
                  d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0);
                  event->acceptProposedAction();
               } else {
                  d->dropIndicatorRect = QRect();
               }
               break;
            case BelowItem:
               if (d->isIndexDropEnabled(index.parent())) {
                  d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0);
                  event->acceptProposedAction();
               } else {
                  d->dropIndicatorRect = QRect();
               }
               break;
            case OnItem:
               if (d->isIndexDropEnabled(index)) {
                  d->dropIndicatorRect = rect;
                  event->acceptProposedAction();
               } else {
                  d->dropIndicatorRect = QRect();
               }
               break;
            case OnViewport:
               d->dropIndicatorRect = QRect();
               if (d->isIndexDropEnabled(rootIndex())) {
                  event->acceptProposedAction(); // allow dropping in empty areas
               }
               break;
         }

      } else {
         d->dropIndicatorRect = QRect();
         d->dropIndicatorPosition = OnViewport;
         if (d->isIndexDropEnabled(rootIndex())) {
            event->acceptProposedAction(); // allow dropping in empty areas
         }
      }
      d->viewport->update();
   } // can decode

   if (d->shouldAutoScroll(event->pos())) {
      startAutoScroll();
   }
}

/*!
    \internal
    Return true if this is a move from ourself and \a index is a child of the selection that
    is being moved.
 */
bool QAbstractItemViewPrivate::droppingOnItself(QDropEvent *event, const QModelIndex &index)
{
   Q_Q(QAbstractItemView);
   Qt::DropAction dropAction = event->dropAction();
   if (q->dragDropMode() == QAbstractItemView::InternalMove) {
      dropAction = Qt::MoveAction;
   }
   if (event->source() == q
      && event->possibleActions() & Qt::MoveAction
      && dropAction == Qt::MoveAction) {
      QModelIndexList selectedIndexes = q->selectedIndexes();
      QModelIndex child = index;
      while (child.isValid() && child != root) {
         if (selectedIndexes.contains(child)) {
            return true;
         }
         child = child.parent();
      }
   }
   return false;
}

void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *)
{
   Q_D(QAbstractItemView);
   stopAutoScroll();
   setState(NoState);
   d->hover = QModelIndex();
   d->viewport->update();
}

void QAbstractItemView::dropEvent(QDropEvent *event)
{
   Q_D(QAbstractItemView);
   if (dragDropMode() == InternalMove) {
      if (event->source() != this || !(event->possibleActions() & Qt::MoveAction)) {
         return;
      }
   }

   QModelIndex index;
   int col = -1;
   int row = -1;
   if (d->dropOn(event, &row, &col, &index)) {

      const Qt::DropAction action = dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction();
      if (d->model->dropMimeData(event->mimeData(), action, row, col, index)) {
         if (action != event->dropAction()) {
            event->setDropAction(action);
            event->accept();
         } else {
            event->acceptProposedAction();
         }
      }
   }
   stopAutoScroll();
   setState(NoState);
   d->viewport->update();
}

/*!
    If the event hasn't already been accepted, determines the index to drop on.

    if (row == -1 && col == -1)
        // append to this drop index
    else
        // place at row, col in drop index

    If it returns true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop.
    \internal
  */
bool QAbstractItemViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex)
{
   Q_Q(QAbstractItemView);
   if (event->isAccepted()) {
      return false;
   }

   QModelIndex index;
   // rootIndex() (i.e. the viewport) might be a valid index
   if (viewport->rect().contains(event->pos())) {
      index = q->indexAt(event->pos());
      if (!index.isValid() || !q->visualRect(index).contains(event->pos())) {
         index = root;
      }
   }

   // If we are allowed to do the drop
   if (model->supportedDropActions() & event->dropAction()) {
      int row = -1;
      int col = -1;
      if (index != root) {
         dropIndicatorPosition = position(event->pos(), q->visualRect(index), index);
         switch (dropIndicatorPosition) {
            case QAbstractItemView::AboveItem:
               row = index.row();
               col = index.column();
               index = index.parent();
               break;
            case QAbstractItemView::BelowItem:
               row = index.row() + 1;
               col = index.column();
               index = index.parent();
               break;
            case QAbstractItemView::OnItem:
            case QAbstractItemView::OnViewport:
               break;
         }
      } else {
         dropIndicatorPosition = QAbstractItemView::OnViewport;
      }
      *dropIndex = index;
      *dropRow = row;
      *dropCol = col;
      if (!droppingOnItself(event, index)) {
         return true;
      }
   }
   return false;
}

QAbstractItemView::DropIndicatorPosition QAbstractItemViewPrivate::position(const QPoint &pos, const QRect &rect,
   const QModelIndex &index) const
{
   QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport;
   if (!overwrite) {
      const int margin = 2;
      if (pos.y() - rect.top() < margin) {
         r = QAbstractItemView::AboveItem;
      } else if (rect.bottom() - pos.y() < margin) {
         r = QAbstractItemView::BelowItem;
      } else if (rect.contains(pos, true)) {
         r = QAbstractItemView::OnItem;
      }
   } else {
      QRect touchingRect = rect;
      touchingRect.adjust(-1, -1, 1, 1);
      if (touchingRect.contains(pos, false)) {
         r = QAbstractItemView::OnItem;
      }
   }

   if (r == QAbstractItemView::OnItem && (!(model->flags(index) & Qt::ItemIsDropEnabled))) {
      r = pos.y() < rect.center().y() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem;
   }

   return r;
}

#endif // QT_NO_DRAGANDDROP

void QAbstractItemView::focusInEvent(QFocusEvent *event)
{
   Q_D(QAbstractItemView);
   QAbstractScrollArea::focusInEvent(event);

   const QItemSelectionModel *model = selectionModel();
   const bool currentIndexValid = currentIndex().isValid();

   if (model
      && !d->currentIndexSet
      && !currentIndexValid) {
      bool autoScroll = d->autoScroll;
      d->autoScroll = false;
      QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index
      if (index.isValid() && d->isIndexEnabled(index) && event->reason() != Qt::MouseFocusReason) {
         selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
      }
      d->autoScroll = autoScroll;
   }

   if (model && currentIndexValid) {
      if (currentIndex().flags() != Qt::ItemIsEditable) {
         setAttribute(Qt::WA_InputMethodEnabled, false);
      } else {
         setAttribute(Qt::WA_InputMethodEnabled);
      }
   }

   if (!currentIndexValid) {
      setAttribute(Qt::WA_InputMethodEnabled, false);
   }

   d->viewport->update();
}

/*!
    This function is called with the given \a event when the widget
    looses the focus. By default, the event is ignored.

    \sa clearFocus(), focusInEvent()
*/
void QAbstractItemView::focusOutEvent(QFocusEvent *event)
{
   Q_D(QAbstractItemView);
   QAbstractScrollArea::focusOutEvent(event);
   d->viewport->update();
}

/*!
    This function is called with the given \a event when a key event is sent to
    the widget. The default implementation handles basic cursor movement, e.g. Up,
    Down, Left, Right, Home, PageUp, and PageDown; the activated() signal is
    emitted if the current index is valid and the activation key is pressed
    (e.g. Enter or Return, depending on the platform).
    This function is where editing is initiated by key press, e.g. if F2 is
    pressed.

    \sa edit(), moveCursor(), keyboardSearch(), tabKeyNavigation
*/
void QAbstractItemView::keyPressEvent(QKeyEvent *event)
{
   Q_D(QAbstractItemView);
   d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling

#ifdef QT_KEYPAD_NAVIGATION
   switch (event->key()) {
      case Qt::Key_Select:
         if (QApplication::keypadNavigationEnabled()) {
            if (!hasEditFocus()) {
               setEditFocus(true);
               return;
            }
         }
         break;

      case Qt::Key_Back:
         if (QApplication::keypadNavigationEnabled() && hasEditFocus()) {
            setEditFocus(false);
         } else {
            event->ignore();
         }
         return;
      case Qt::Key_Down:
      case Qt::Key_Up:
         // Let's ignore vertical navigation events, only if there is no other widget
         // what can take the focus in vertical direction. This means widget can handle navigation events
         // even the widget don't have edit focus, and there is no other widget in requested direction.
         if (QApplication::keypadNavigationEnabled() && !hasEditFocus()
            && QWidgetPrivate::canKeypadNavigate(Qt::Vertical)) {
            event->ignore();
            return;
         }
         break;
      case Qt::Key_Left:
      case Qt::Key_Right:
         // Similar logic as in up and down events
         if (QApplication::keypadNavigationEnabled() && !hasEditFocus()
            && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) || QWidgetPrivate::inTabWidget(this))) {
            event->ignore();
            return;
         }
         break;
      default:
         if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
            event->ignore();
            return;
         }
   }
#endif

#if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT)
   if (event == QKeySequence::Copy) {
      QVariant variant;
      if (d->model) {
         variant = d->model->data(currentIndex(), Qt::DisplayRole);
      }
      if (variant.type() == QVariant::String) {
         QApplication::clipboard()->setText(variant.toString());
      }
      event->accept();
   }
#endif

   QPersistentModelIndex newCurrent;
   d->moveCursorUpdatedView = false;
   switch (event->key()) {
      case Qt::Key_Down:
         newCurrent = moveCursor(MoveDown, event->modifiers());
         break;
      case Qt::Key_Up:
         newCurrent = moveCursor(MoveUp, event->modifiers());
         break;
      case Qt::Key_Left:
         newCurrent = moveCursor(MoveLeft, event->modifiers());
         break;
      case Qt::Key_Right:
         newCurrent = moveCursor(MoveRight, event->modifiers());
         break;
      case Qt::Key_Home:
         newCurrent = moveCursor(MoveHome, event->modifiers());
         break;
      case Qt::Key_End:
         newCurrent = moveCursor(MoveEnd, event->modifiers());
         break;
      case Qt::Key_PageUp:
         newCurrent = moveCursor(MovePageUp, event->modifiers());
         break;
      case Qt::Key_PageDown:
         newCurrent = moveCursor(MovePageDown, event->modifiers());
         break;
      case Qt::Key_Tab:
         if (d->tabKeyNavigation) {
            newCurrent = moveCursor(MoveNext, event->modifiers());
         }
         break;
      case Qt::Key_Backtab:
         if (d->tabKeyNavigation) {
            newCurrent = moveCursor(MovePrevious, event->modifiers());
         }
         break;
   }

   QPersistentModelIndex oldCurrent = currentIndex();
   if (newCurrent != oldCurrent && newCurrent.isValid() && d->isIndexEnabled(newCurrent)) {
      if (!hasFocus() && QApplication::focusWidget() == indexWidget(oldCurrent)) {
         setFocus();
      }

      QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, event);
      if (command != QItemSelectionModel::NoUpdate
         || style()->styleHint(QStyle::SH_ItemView_MovementWithoutUpdatingSelection, 0, this)) {
         // note that we don't check if the new current index is enabled because moveCursor() makes sure it is
         if (command & QItemSelectionModel::Current) {
            d->selectionModel->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
            if (!d->currentSelectionStartIndex.isValid()) {
               d->currentSelectionStartIndex = oldCurrent;
            }
            QRect rect(visualRect(d->currentSelectionStartIndex).center(), visualRect(newCurrent).center());
            setSelection(rect, command);
         } else {
            d->selectionModel->setCurrentIndex(newCurrent, command);
            d->currentSelectionStartIndex = newCurrent;
            if (newCurrent.isValid()) {
               // We copy the same behaviour as for mousePressEvent().
               QRect rect(visualRect(newCurrent).center(), QSize(1, 1));
               setSelection(rect, command);
            }
         }
         event->accept();
         return;
      }
   }

   switch (event->key()) {
      // ignored keys
      case Qt::Key_Down:
      case Qt::Key_Up:
#ifdef QT_KEYPAD_NAVIGATION
         if (QApplication::keypadNavigationEnabled() && QWidgetPrivate::canKeypadNavigate(Qt::Vertical)) {
            event->accept(); // don't change focus
            break;
         }
#endif
      case Qt::Key_Left:
      case Qt::Key_Right:
#ifdef QT_KEYPAD_NAVIGATION
         if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
            && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal)
               || (QWidgetPrivate::inTabWidget(this) && d->model->columnCount(d->root) > 1))) {
            event->accept(); // don't change focus
            break;
         }
#endif // QT_KEYPAD_NAVIGATION
      case Qt::Key_Home:
      case Qt::Key_End:
      case Qt::Key_PageUp:
      case Qt::Key_PageDown:
      case Qt::Key_Escape:
      case Qt::Key_Shift:
      case Qt::Key_Control:
      case Qt::Key_Delete:
      case Qt::Key_Backspace:
         event->ignore();
         break;
      case Qt::Key_Space:
      case Qt::Key_Select:
         if (!edit(currentIndex(), AnyKeyPressed, event) && d->selectionModel) {
            d->selectionModel->select(currentIndex(), selectionCommand(currentIndex(), event));
         }
#ifdef QT_KEYPAD_NAVIGATION
         if ( event->key() == Qt::Key_Select ) {
            // Also do Key_Enter action.
            if (currentIndex().isValid()) {
               if (state() != EditingState) {
                  emit activated(currentIndex());
               }
            } else {
               event->ignore();
            }
         }
#endif
         break;

#ifdef Q_OS_DARWIN
      case Qt::Key_Enter:
      case Qt::Key_Return:
         // Propagate the enter if you couldn't edit the item and there are no
         // current editors (if there are editors, the event was most likely propagated from it).
         if (!edit(currentIndex(), EditKeyPressed, event) && d->editorIndexHash.isEmpty()) {
            event->ignore();
         }
         break;
#else
      case Qt::Key_F2:
         if (!edit(currentIndex(), EditKeyPressed, event)) {
            event->ignore();
         }
         break;
      case Qt::Key_Enter:
      case Qt::Key_Return:
         // ### we can't open the editor on enter, becuse
         // some widgets will forward the enter event back
         // to the viewport, starting an endless loop
         if (state() != EditingState || hasFocus()) {
            if (currentIndex().isValid()) {
               emit activated(currentIndex());
            }
            event->ignore();
         }
         break;
#endif
      default: {
         if (event == QKeySequence::SelectAll && selectionMode() != NoSelection) {
            selectAll();
            break;
         }
#ifdef Q_OS_DARWIN
         if (event->key() == Qt::Key_O && event->modifiers() & Qt::ControlModifier && currentIndex().isValid()) {
            emit activated(currentIndex());
            break;
         }
#endif
         bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
         if (!event->text().isEmpty() && !modified && !edit(currentIndex(), AnyKeyPressed, event)) {
            keyboardSearch(event->text());
            event->accept();
         } else {
            event->ignore();
         }
         break;
      }
   }
   if (d->moveCursorUpdatedView) {
      event->accept();
   }
}

/*!
    This function is called with the given \a event when a resize event is sent to
    the widget.

    \sa QWidget::resizeEvent()
*/
void QAbstractItemView::resizeEvent(QResizeEvent *event)
{
   QAbstractScrollArea::resizeEvent(event);
   updateGeometries();
}

/*!
  This function is called with the given \a event when a timer event is sent
  to the widget.

  \sa QObject::timerEvent()
*/
void QAbstractItemView::timerEvent(QTimerEvent *event)
{
   Q_D(QAbstractItemView);
   if (event->timerId() == d->fetchMoreTimer.timerId()) {
      d->fetchMore();
   } else if (event->timerId() == d->delayedReset.timerId()) {
      reset();
   } else if (event->timerId() == d->autoScrollTimer.timerId()) {
      doAutoScroll();
   } else if (event->timerId() == d->updateTimer.timerId()) {
      d->updateDirtyRegion();
   } else if (event->timerId() == d->delayedEditing.timerId()) {
      d->delayedEditing.stop();
      edit(currentIndex());
   } else if (event->timerId() == d->delayedLayout.timerId()) {
      d->delayedLayout.stop();
      if (isVisible()) {
         d->interruptDelayedItemsLayout();
         doItemsLayout();
         const QModelIndex current = currentIndex();
         if (current.isValid() && d->state == QAbstractItemView::EditingState) {
            scrollTo(current);
         }
      }
   } else if (event->timerId() == d->delayedAutoScroll.timerId()) {
      d->delayedAutoScroll.stop();
      //end of the timer: if the current item is still the same as the one when the mouse press occurred
      //we only get here if there was no double click
      if (d->pressedIndex.isValid() && d->pressedIndex == currentIndex()) {
         scrollTo(d->pressedIndex);
      }
   }
}

/*!
    \reimp
*/
void QAbstractItemView::inputMethodEvent(QInputMethodEvent *event)
{
   if (event->commitString().isEmpty() && event->preeditString().isEmpty()) {
      event->ignore();
      return;
   }
   if (!edit(currentIndex(), AnyKeyPressed, event)) {
      if (!event->commitString().isEmpty()) {
         keyboardSearch(event->commitString());
      }
      event->ignore();
   }
}

#ifndef QT_NO_DRAGANDDROP

QAbstractItemView::DropIndicatorPosition QAbstractItemView::dropIndicatorPosition() const
{
   Q_D(const QAbstractItemView);
   return d->dropIndicatorPosition;
}
#endif

/*!
    This convenience function returns a list of all selected and
    non-hidden item indexes in the view. The list contains no
    duplicates, and is not sorted.

    \sa QItemSelectionModel::selectedIndexes()
*/
QModelIndexList QAbstractItemView::selectedIndexes() const
{
   Q_D(const QAbstractItemView);
   QModelIndexList indexes;
   if (d->selectionModel) {
      indexes = d->selectionModel->selectedIndexes();
      QList<QModelIndex>::iterator it = indexes.begin();
      while (it != indexes.end())
         if (isIndexHidden(*it)) {
            it = indexes.erase(it);
         } else {
            ++it;
         }
   }
   return indexes;
}

bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
{
   Q_D(QAbstractItemView);

   if (! d->isIndexValid(index)) {
      return false;
   }

   if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget *>(0) : d->editorForIndex(index).widget.data())) {
      if (w->focusPolicy() == Qt::NoFocus) {
         return false;
      }
      w->setFocus();
      return true;
   }

   if (trigger == DoubleClicked) {
      d->delayedEditing.stop();
      d->delayedAutoScroll.stop();

   } else if (trigger == CurrentChanged) {
      d->delayedEditing.stop();
   }

   if (d->sendDelegateEvent(index, event)) {
      update(index);
      return true;
   }

   // save the previous trigger before updating
   EditTriggers lastTrigger = d->lastTrigger;
   d->lastTrigger = trigger;

   if (!d->shouldEdit(trigger, d->model->buddy(index))) {
      return false;
   }

   if (d->delayedEditing.isActive()) {
      return false;
   }

   // we will receive a mouseButtonReleaseEvent after a
   // mouseDoubleClickEvent, so we need to check the previous trigger
   if (lastTrigger == DoubleClicked && trigger == SelectedClicked) {
      return false;
   }

   // we may get a double click event later
   if (trigger == SelectedClicked) {
      d->delayedEditing.start(QApplication::doubleClickInterval(), this);
   } else {
      d->openEditor(index, d->shouldForwardEvent(trigger, event) ? event : 0);
   }

   return true;
}

/*!
    \internal
    Updates the data shown in the open editor widgets in the view.
*/
void QAbstractItemView::updateEditorData()
{
   Q_D(QAbstractItemView);
   d->updateEditorData(QModelIndex(), QModelIndex());
}

/*!
    \internal
    Updates the geometry of the open editor widgets in the view.
*/
void QAbstractItemView::updateEditorGeometries()
{
   Q_D(QAbstractItemView);

   if (d->editorIndexHash.isEmpty()) {
      return;
   }

   if (d->delayedPendingLayout) {
      d->executePostedLayout();
      return;
   }

   QStyleOptionViewItem option = d->viewOptions();
   QEditorIndexHash::iterator it = d->editorIndexHash.begin();
   QWidgetList editorsToRelease;
   QWidgetList editorsToHide;

   while (it != d->editorIndexHash.end()) {
      QModelIndex index = it.value();
      QWidget *editor = it.key();

      if (index.isValid() && editor) {
         option.rect = visualRect(index);
         if (option.rect.isValid()) {
            editor->show();
            QAbstractItemDelegate *delegate = d->delegateForIndex(index);
            if (delegate) {
               delegate->updateEditorGeometry(editor, option, index);
            }
         } else {
            editorsToHide << editor;
         }
         ++it;

      } else {
         d->indexEditorHash.remove(it.value());
         it = d->editorIndexHash.erase(it);
         editorsToRelease << editor;
      }
   }

   //we hide and release the editor outside of the loop because it might change the focus and try
   //to change the editors hashes.
   for (int i = 0; i < editorsToHide.count(); ++i) {
      editorsToHide.at(i)->hide();
   }
   for (int i = 0; i < editorsToRelease.count(); ++i) {
      d->releaseEditor(editorsToRelease.at(i));
   }
}

/*!
    \since 4.4

    Updates the geometry of the child widgets of the view.
*/
void QAbstractItemView::updateGeometries()
{
   Q_D(QAbstractItemView);
   updateEditorGeometries();
   d->fetchMoreTimer.start(0, this); //fetch more later
   d->updateGeometry();
}

/*!
    \internal
*/
void QAbstractItemView::verticalScrollbarValueChanged(int value)
{
   Q_D(QAbstractItemView);

   if (verticalScrollBar()->maximum() == value && d->model->canFetchMore(d->root)) {
      d->model->fetchMore(d->root);
   }

   QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos());
   if (viewport()->rect().contains(posInVp)) {
      d->checkMouseMove(posInVp);
   }
}

/*!
    \internal
*/
void QAbstractItemView::horizontalScrollbarValueChanged(int value)
{
   Q_D(QAbstractItemView);
   if (horizontalScrollBar()->maximum() == value && d->model->canFetchMore(d->root)) {
      d->model->fetchMore(d->root);
   }
   QPoint posInVp = viewport()->mapFromGlobal(QCursor::pos());
   if (viewport()->rect().contains(posInVp)) {
      d->checkMouseMove(posInVp);
   }
}

void QAbstractItemView::verticalScrollbarAction(int)
{
   //do nothing
}

void QAbstractItemView::horizontalScrollbarAction(int)
{
   //do nothing
}

void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
{
   Q_D(QAbstractItemView);

   // Close the editor
   if (editor) {
      bool isPersistent = d->persistent.contains(editor);
      bool hadFocus = editor->hasFocus();
      QModelIndex index = d->indexForEditor(editor);

      if (!index.isValid()) {
         return;   // the editor was not registered
      }

      if (!isPersistent) {
         setState(NoState);
         QModelIndex index = d->indexForEditor(editor);
         editor->removeEventFilter(d->delegateForIndex(index));
         d->removeEditor(editor);
      }

      if (hadFocus) {
         if (focusPolicy() != Qt::NoFocus) {
            setFocus();   // this will send a focusLost event to the editor
         } else {
            editor->clearFocus();
         }
      } else {
         d->checkPersistentEditorFocus();
      }

      QPointer<QWidget> ed = editor;
      QApplication::sendPostedEvents(editor, 0);
      editor = ed;

      if (!isPersistent && editor) {
         d->releaseEditor(editor, index);
      }
   }

   // The EndEditHint part
   QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::NoUpdate;
   if (d->selectionMode != NoSelection) {
      flags = QItemSelectionModel::ClearAndSelect | d->selectionBehaviorFlags();
   }

   switch (hint) {
      case QAbstractItemDelegate::EditNextItem: {
         QModelIndex index = moveCursor(MoveNext, Qt::NoModifier);
         if (index.isValid()) {
            QPersistentModelIndex persistent(index);
            d->selectionModel->setCurrentIndex(persistent, flags);
            // currentChanged signal would have already started editing
            if (index.flags() & Qt::ItemIsEditable
               && (!(editTriggers() & QAbstractItemView::CurrentChanged))) {
               edit(persistent);
            }
         }
         break;
      }

      case QAbstractItemDelegate::EditPreviousItem: {
         QModelIndex index = moveCursor(MovePrevious, Qt::NoModifier);
         if (index.isValid()) {
            QPersistentModelIndex persistent(index);
            d->selectionModel->setCurrentIndex(persistent, flags);
            // currentChanged signal would have already started editing
            if (index.flags() & Qt::ItemIsEditable
               && (!(editTriggers() & QAbstractItemView::CurrentChanged))) {
               edit(persistent);
            }
         }
         break;
      }

      case QAbstractItemDelegate::SubmitModelCache:
         d->model->submit();
         break;

      case QAbstractItemDelegate::RevertModelCache:
         d->model->revert();
         break;

      default:
         break;
   }
}

/*!
    Commit the data in the \a editor to the model.

    \sa closeEditor()
*/
void QAbstractItemView::commitData(QWidget *editor)
{
   Q_D(QAbstractItemView);

   if (!editor || !d->itemDelegate || d->currentlyCommittingEditor) {
      return;
   }

   QModelIndex index = d->indexForEditor(editor);
   if (!index.isValid()) {
      return;
   }

   d->currentlyCommittingEditor = editor;
   QAbstractItemDelegate *delegate = d->delegateForIndex(index);
   editor->removeEventFilter(delegate);
   delegate->setModelData(editor, d->model, index);
   editor->installEventFilter(delegate);
   d->currentlyCommittingEditor = 0;
}

void QAbstractItemView::editorDestroyed(QObject *editor)
{
   Q_D(QAbstractItemView);

   QWidget *w = dynamic_cast<QWidget *>(editor);
   d->removeEditor(w);
   d->persistent.remove(w);

   if (state() == EditingState) {
      setState(NoState);
   }
}

/*!
    \obsolete
*/
void QAbstractItemView::setHorizontalStepsPerItem(int steps)
{
   (void) steps;
}

/*!
    \obsolete
*/
int QAbstractItemView::horizontalStepsPerItem() const
{
   return 1;
}

/*!
    \obsolete
*/
void QAbstractItemView::setVerticalStepsPerItem(int steps)
{
   (void) steps;
}

/*!
    \obsolete
*/
int QAbstractItemView::verticalStepsPerItem() const
{
   return 1;
}

void QAbstractItemView::keyboardSearch(const QString &search)
{
   Q_D(QAbstractItemView);

   if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root)) {
      return;
   }

   QModelIndex start = currentIndex().isValid() ? currentIndex()
      : d->model->index(0, 0, d->root);

   bool skipRow = false;
   bool keyboardTimeWasValid = d->keyboardInputTime.isValid();
   qint64 keyboardInputTimeElapsed;

   if (keyboardTimeWasValid) {
      keyboardInputTimeElapsed = d->keyboardInputTime.restart();
   } else {
      d->keyboardInputTime.start();
   }
   if (search.isEmpty() || !keyboardTimeWasValid
      || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) {
      d->keyboardInput = search;
      skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0)
   } else {
      d->keyboardInput += search;
   }

   // special case for searches with same key like 'aaaaa'
   bool sameKey = false;
   if (d->keyboardInput.length() > 1) {
      int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1));
      sameKey = (c == d->keyboardInput.length());
      if (sameKey) {
         skipRow = true;
      }
   }

   // skip if we are searching for the same key or a new search started
   if (skipRow) {
      QModelIndex parent = start.parent();
      int newRow = (start.row() < d->model->rowCount(parent) - 1) ? start.row() + 1 : 0;
      start = d->model->index(newRow, start.column(), parent);
   }

   // search from start with wraparound
   QModelIndex current = start;
   QModelIndexList match;
   QModelIndex firstMatch;
   QModelIndex startMatch;
   QModelIndexList previous;
   do {
      match = d->model->match(current, Qt::DisplayRole, d->keyboardInput);
      if (match == previous) {
         break;
      }
      firstMatch = match.value(0);
      previous = match;
      if (firstMatch.isValid()) {
         if (d->isIndexEnabled(firstMatch)) {
            setCurrentIndex(firstMatch);
            break;
         }
         int row = firstMatch.row() + 1;
         if (row >= d->model->rowCount(firstMatch.parent())) {
            row = 0;
         }
         current = firstMatch.sibling(row, firstMatch.column());

         //avoid infinite loop if all the matching items are disabled.
         if (!startMatch.isValid()) {
            startMatch = firstMatch;
         } else if (startMatch == firstMatch) {
            break;
         }
      }
   } while (current != start && firstMatch.isValid());
}

QSize QAbstractItemView::sizeHintForIndex(const QModelIndex &index) const
{
   Q_D(const QAbstractItemView);

   if (!d->isIndexValid(index) || ! d->itemDelegate) {
      return QSize();
   }

   return d->delegateForIndex(index)->sizeHint(d->viewOptions(), index);
}

int QAbstractItemView::sizeHintForRow(int row) const
{
   Q_D(const QAbstractItemView);

   if (row < 0 || row >= d->model->rowCount(d->root)) {
      return -1;
   }

   ensurePolished();

   QStyleOptionViewItem option = d->viewOptions();
   int height   = 0;
   int colCount = d->model->columnCount(d->root);

   for (int c = 0; c < colCount; ++c) {
      const QModelIndex index = d->model->index(row, c, d->root);

      if (QWidget *editor = d->editorForIndex(index).widget.data()) {
         height = qMax(height, editor->height());
      }

      if (const QAbstractItemDelegate *delegate = d->delegateForIndex(index)) {
         height = qMax(height, delegate->sizeHint(option, index).height());
      }
   }

   return height;
}

int QAbstractItemView::sizeHintForColumn(int column) const
{
   Q_D(const QAbstractItemView);

   if (column < 0 || column >= d->model->columnCount(d->root)) {
      return -1;
   }

   ensurePolished();

   QStyleOptionViewItem option = d->viewOptions();
   int width = 0;
   int rows = d->model->rowCount(d->root);

   for (int r = 0; r < rows; ++r) {
      const QModelIndex index = d->model->index(r, column, d->root);
      if (QWidget *editor = d->editorForIndex(index).widget.data()) {
         width = qMax(width, editor->sizeHint().width());
      }

      if (const QAbstractItemDelegate *delegate = d->delegateForIndex(index)) {
         width = qMax(width, delegate->sizeHint(option, index).width());
      }

   }
   return width;
}

/*!
    Opens a persistent editor on the item at the given \a index.
    If no editor exists, the delegate will create a new editor.

    \sa closePersistentEditor()
*/
void QAbstractItemView::openPersistentEditor(const QModelIndex &index)
{
   Q_D(QAbstractItemView);
   QStyleOptionViewItem options = d->viewOptions();
   options.rect = visualRect(index);
   options.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);

   QWidget *editor = d->editor(index, options);
   if (editor) {
      editor->show();
      d->persistent.insert(editor);
   }
}

/*!
    Closes the persistent editor for the item at the given \a index.

    \sa openPersistentEditor()
*/
void QAbstractItemView::closePersistentEditor(const QModelIndex &index)
{
   Q_D(QAbstractItemView);

   if (QWidget *editor = d->editorForIndex(index).widget.data()) {

      if (index == selectionModel()->currentIndex()) {
         closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
      }

      d->persistent.remove(editor);
      d->removeEditor(editor);
      d->releaseEditor(editor, index);
   }
}

/*!
    \since 4.1

    Sets the given \a widget on the item at the given \a index, passing the
    ownership of the widget to the viewport.

    If \a index is invalid (e.g., if you pass the root index), this function
    will do nothing.

    The given \a widget's \l{QWidget}{autoFillBackground} property must be set
    to true, otherwise the widget's background will be transparent, showing
    both the model data and the item at the given \a index.

    If index widget A is replaced with index widget B, index widget A will be
    deleted. For example, in the code snippet below, the QLineEdit object will
    be deleted.

    \snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 1

    This function should only be used to display static content within the
    visible area corresponding to an item of data. If you want to display
    custom dynamic content or implement a custom editor widget, subclass
    QItemDelegate instead.

    \sa {Delegate Classes}
*/
void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget)
{
   Q_D(QAbstractItemView);

   if (!d->isIndexValid(index)) {
      return;
   }

   if (indexWidget(index) == widget) {
      return;
   }

   if (QWidget *oldWidget = indexWidget(index)) {
      d->persistent.remove(oldWidget);
      d->removeEditor(oldWidget);
      oldWidget->deleteLater();
   }

   if (widget) {
      widget->setParent(viewport());
      d->persistent.insert(widget);
      d->addEditor(index, widget, true);
      widget->show();
      dataChanged(index, index); // update the geometry
      if (!d->delayedPendingLayout) {
         widget->setGeometry(visualRect(index));
      }
   }
}

QWidget *QAbstractItemView::indexWidget(const QModelIndex &index) const
{
   Q_D(const QAbstractItemView);
   if (d->isIndexValid(index))
      if (QWidget *editor = d->editorForIndex(index).widget.data()) {
         return editor;
      }

   return 0;
}

void QAbstractItemView::scrollToTop()
{
   verticalScrollBar()->setValue(verticalScrollBar()->minimum());
}

void QAbstractItemView::scrollToBottom()
{
   Q_D(QAbstractItemView);

   if (d->delayedPendingLayout) {
      d->executePostedLayout();
      updateGeometries();
   }

   verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}

void QAbstractItemView::update(const QModelIndex &index)
{
   Q_D(QAbstractItemView);
   if (index.isValid()) {
      const QRect rect = visualRect(index);
      //this test is important for peformance reason
      //For example in dataChanged we simply update all the cells without checking
      //it can be a major bottleneck to update rects that aren't even part of the viewport
      if (d->viewport->rect().intersects(rect)) {
         d->viewport->update(rect);
      }
   }
}

/*!
    This slot is called when items are changed in the model. The
    changed items are those from \a topLeft to \a bottomRight
    inclusive. If just one item is changed \a topLeft == \a
    bottomRight.
*/
void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
   (void) roles;

   // Single item changed
   Q_D(QAbstractItemView);

   if (topLeft == bottomRight && topLeft.isValid()) {
      const QEditorInfo &editorInfo = d->editorForIndex(topLeft);

      //we don't update the edit data if it is static
      if (!editorInfo.isStatic && editorInfo.widget) {
         QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft);
         if (delegate) {
            delegate->setEditorData(editorInfo.widget.data(), topLeft);
         }
      }

      if (isVisible() && !d->delayedPendingLayout) {
         // otherwise the items will be update later anyway
         update(topLeft);
      }


   } else {
      d->updateEditorData(topLeft, bottomRight);

      if (isVisible() && !d->delayedPendingLayout) {
         d->viewport->update();
      }
   }

#ifndef QT_NO_ACCESSIBILITY
   if (QAccessible::isActive()) {
      QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged);
      accessibleEvent.setFirstRow(topLeft.row());
      accessibleEvent.setFirstColumn(topLeft.column());
      accessibleEvent.setLastRow(bottomRight.row());
      accessibleEvent.setLastColumn(bottomRight.column());
      QAccessible::updateAccessibility(&accessibleEvent);
   }
#endif
   d->updateGeometry();
}

void QAbstractItemView::rowsInserted(const QModelIndex &, int, int)
{
   if (!isVisible()) {
      d_func()->fetchMoreTimer.start(0, this);   //fetch more later
   } else {
      updateEditorGeometries();
   }
}

void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
   Q_D(QAbstractItemView);

   setState(CollapsingState);

   // Ensure one selected item in single selection mode.
   QModelIndex current = currentIndex();
   if (d->selectionMode == SingleSelection && current.isValid() && current.row() >= start && current.row() <= end
      && current.parent() == parent) {

      int totalToRemove = end - start + 1;

      if (d->model->rowCount(parent) <= totalToRemove) { // no more children
         QModelIndex index = parent;
         while (index != d->root && !d->isIndexEnabled(index)) {
            index = index.parent();
         }

         if (index != d->root) {
            setCurrentIndex(index);
         }

      } else {
         int row = end + 1;
         QModelIndex next;

         do { // find the next visible and enabled item
            next = d->model->index(row++, current.column(), current.parent());
         } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next)));

         if (row > d->model->rowCount(parent)) {
            row = start - 1;

            do { // find the previous visible and enabled item
               next = d->model->index(row--, current.column(), current.parent());
            } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next)));
         }
         setCurrentIndex(next);
      }
   }

   // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
   QEditorIndexHash::iterator i = d->editorIndexHash.begin();
   while (i != d->editorIndexHash.end()) {
      const QModelIndex index = i.value();
      if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) {
         QWidget *editor = i.key();
         QEditorInfo info = d->indexEditorHash.take(index);
         i = d->editorIndexHash.erase(i);

         if (info.widget) {
            d->releaseEditor(editor, index);
         }
      } else {
         ++i;
      }
   }
}

void QAbstractItemViewPrivate::_q_rowsRemoved(const QModelIndex &index, int start, int end)
{
  (void) index;

   Q_Q(QAbstractItemView);

   if (q->isVisible()) {
      q->updateEditorGeometries();
   }
   q->setState(QAbstractItemView::NoState);

#ifndef QT_NO_ACCESSIBILITY

   if (QAccessible::isActive()) {
      QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::RowsRemoved);
      accessibleEvent.setFirstRow(start);
      accessibleEvent.setLastRow(end);
      QAccessible::updateAccessibility(&accessibleEvent);
   }
#endif

   updateGeometry();
}

/*!
    \internal

    This slot is called when columns are about to be removed. The deleted
    columns are those under the given \a parent from \a start to \a end
    inclusive.
*/
void QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
   Q_Q(QAbstractItemView);

   q->setState(QAbstractItemView::CollapsingState);

   // Ensure one selected item in single selection mode.
   QModelIndex current = q->currentIndex();
   if (current.isValid()
      && selectionMode == QAbstractItemView::SingleSelection
      && current.column() >= start
      && current.column() <= end) {

      int totalToRemove = end - start + 1;

      if (model->columnCount(parent) < totalToRemove) { // no more columns
         QModelIndex index = parent;
         while (index.isValid() && !isIndexEnabled(index)) {
            index = index.parent();
         }
         if (index.isValid()) {
            q->setCurrentIndex(index);
         }

      } else {
         int column = end;
         QModelIndex next;
         do { // find the next visible and enabled item
            next = model->index(current.row(), column++, current.parent());
         } while (next.isValid() && (q->isIndexHidden(next) || !isIndexEnabled(next)));
         q->setCurrentIndex(next);
      }
   }

   // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
   QEditorIndexHash::iterator it = editorIndexHash.begin();
   while (it != editorIndexHash.end()) {
      QModelIndex index = it.value();
      if (index.column() <= start && index.column() >= end && model->parent(index) == parent) {
         QWidget *editor = it.key();
         QEditorInfo info = indexEditorHash.take(it.value());
         it = editorIndexHash.erase(it);
         if (info.widget) {
            releaseEditor(editor, index);
         }
      } else {
         ++it;
      }
   }

}

/*!
    \internal

    This slot is called when columns have been removed. The deleted
    rows are those under the given \a parent from \a start to \a end
    inclusive.
*/
void QAbstractItemViewPrivate::_q_columnsRemoved(const QModelIndex &index, int start, int end)
{
  (void) index;

   Q_Q(QAbstractItemView);
   if (q->isVisible()) {
      q->updateEditorGeometries();
   }

   q->setState(QAbstractItemView::NoState);

#ifndef QT_NO_ACCESSIBILITY
   if (QAccessible::isActive()) {
      QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ColumnsRemoved);
      accessibleEvent.setFirstColumn(start);
      accessibleEvent.setLastColumn(end);
      QAccessible::updateAccessibility(&accessibleEvent);
   }
#endif

   updateGeometry();
}

// internal
void QAbstractItemViewPrivate::_q_rowsInserted(const QModelIndex &index, int start, int end)
{
  (void) index;

#ifndef QT_NO_ACCESSIBILITY

   Q_Q(QAbstractItemView);

   if (QAccessible::isActive()) {
      QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::RowsInserted);
      accessibleEvent.setFirstRow(start);
      accessibleEvent.setLastRow(end);
      QAccessible::updateAccessibility(&accessibleEvent);
   }
#endif

   updateGeometry();
}

// internal
void QAbstractItemViewPrivate::_q_columnsInserted(const QModelIndex &index, int start, int end)
{
  (void) index;

   Q_Q(QAbstractItemView);

   if (q->isVisible()) {
      q->updateEditorGeometries();
   }

#ifndef QT_NO_ACCESSIBILITY
   if (QAccessible::isActive()) {
      QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ColumnsInserted);
      accessibleEvent.setFirstColumn(start);
      accessibleEvent.setLastColumn(end);
      QAccessible::updateAccessibility(&accessibleEvent);
   }
#endif

   updateGeometry();
}

// internal
void QAbstractItemViewPrivate::_q_modelDestroyed()
{
   model = QAbstractItemModelPrivate::staticEmptyModel();
   doDelayedReset();
}

// internal
void QAbstractItemViewPrivate::_q_layoutChanged()
{
   doDelayedItemsLayout();

#ifndef QT_NO_ACCESSIBILITY
   Q_Q(QAbstractItemView);

   if (QAccessible::isActive()) {
      QAccessibleTableModelChangeEvent accessibleEvent(q, QAccessibleTableModelChangeEvent::ModelReset);
      QAccessible::updateAccessibility(&accessibleEvent);
   }
#endif
}

void QAbstractItemViewPrivate::_q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)
{
   _q_layoutChanged();
}
void QAbstractItemViewPrivate::_q_columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)
{
   _q_layoutChanged();
}

void QAbstractItemView::selectionChanged(const QItemSelection &selected,
   const QItemSelection &deselected)
{
   Q_D(QAbstractItemView);
   if (isVisible() && updatesEnabled()) {
      d->viewport->update(visualRegionForSelection(deselected) | visualRegionForSelection(selected));
   }
}

void QAbstractItemView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
   Q_D(QAbstractItemView);
   Q_ASSERT(d->model);

   if (previous.isValid()) {
      QModelIndex buddy = d->model->buddy(previous);
      QWidget *editor = d->editorForIndex(buddy).widget.data();
      if (editor && !d->persistent.contains(editor)) {
         commitData(editor);
         if (current.row() != previous.row()) {
            closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
         } else {
            closeEditor(editor, QAbstractItemDelegate::NoHint);
         }
      }
      if (isVisible()) {
         update(previous);
      }
   }

   if (current.isValid() && !d->autoScrollTimer.isActive()) {
      if (isVisible()) {
         if (d->autoScroll) {
            scrollTo(current);
         }

         update(current);
         edit(current, CurrentChanged, 0);

         if (current.row() == (d->model->rowCount(d->root) - 1)) {
            d->fetchMore();
         }
      } else {
         d->shouldScrollToCurrentOnShow = d->autoScroll;
      }
   }
}

#ifndef QT_NO_DRAGANDDROP
/*!
    Starts a drag by calling drag->exec() using the given \a supportedActions.
*/
void QAbstractItemView::startDrag(Qt::DropActions supportedActions)
{
   Q_D(QAbstractItemView);
   QModelIndexList indexes = d->selectedDraggableIndexes();
   if (indexes.count() > 0) {
      QMimeData *data = d->model->mimeData(indexes);
      if (!data) {
         return;
      }
      QRect rect;
      QPixmap pixmap = d->renderToPixmap(indexes, &rect);
      rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
      QDrag *drag = new QDrag(this);
      drag->setPixmap(pixmap);
      drag->setMimeData(data);
      drag->setHotSpot(d->pressedPosition - rect.topLeft());
      Qt::DropAction defaultDropAction = Qt::IgnoreAction;
      if (d->defaultDropAction != Qt::IgnoreAction && (supportedActions & d->defaultDropAction)) {
         defaultDropAction = d->defaultDropAction;
      } else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove) {
         defaultDropAction = Qt::CopyAction;
      }

      if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) {
         d->clearOrRemove();
      }

      // Reset the drop indicator
      d->dropIndicatorRect = QRect();
      d->dropIndicatorPosition = OnItem;
   }
}
#endif // QT_NO_DRAGANDDROP

/*!
    Returns a QStyleOptionViewItem structure populated with the view's
    palette, font, state, alignments etc.
*/
QStyleOptionViewItem QAbstractItemView::viewOptions() const
{
   Q_D(const QAbstractItemView);
   QStyleOptionViewItem option;
   option.initFrom(this);

   option.state &= ~QStyle::State_MouseOver;
   option.font = font();

   if (! hasFocus()) {
      option.state &= ~QStyle::State_Active;
   }

   option.state &= ~QStyle::State_HasFocus;
   if (d->iconSize.isValid()) {
      option.decorationSize = d->iconSize;
   } else {
      int pm = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
      option.decorationSize = QSize(pm, pm);
   }
   option.decorationPosition = QStyleOptionViewItem::Left;
   option.decorationAlignment = Qt::AlignCenter;
   option.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter;
   option.textElideMode = d->textElideMode;
   option.rect = QRect();
   option.showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, this);
   if (d->wrapItemText) {
      option.features = QStyleOptionViewItem::WrapText;
   }
   option.locale = locale();
   option.locale.setNumberOptions(QLocale::OmitGroupSeparator);
   option.widget = this;

   return option;
}

QStyleOptionViewItem QAbstractItemViewPrivate::viewOptions() const
{
   Q_Q(const QAbstractItemView);

   return q->viewOptions();
}

/*!
    Returns the item view's state.

    \sa setState()
*/
QAbstractItemView::State QAbstractItemView::state() const
{
   Q_D(const QAbstractItemView);
   return d->state;
}

/*!
    Sets the item view's state to the given \a state.

    \sa state()
*/
void QAbstractItemView::setState(State state)
{
   Q_D(QAbstractItemView);
   d->state = state;
}

/*!
  Schedules a layout of the items in the view to be executed when the
  event processing starts.

  Even if scheduleDelayedItemsLayout() is called multiple times before
  events are processed, the view will only do the layout once.

  \sa executeDelayedItemsLayout()
*/
void QAbstractItemView::scheduleDelayedItemsLayout()
{
   Q_D(QAbstractItemView);
   d->doDelayedItemsLayout();
}

/*!
  Executes the scheduled layouts without waiting for the event processing
  to begin.

  \sa scheduleDelayedItemsLayout()
*/
void QAbstractItemView::executeDelayedItemsLayout()
{
   Q_D(QAbstractItemView);
   d->executePostedLayout();
}

/*!
    \since 4.1

    Marks the given \a region as dirty and schedules it to be updated.
    You only need to call this function if you are implementing
    your own view subclass.

    \sa scrollDirtyRegion(), dirtyRegionOffset()
*/

void QAbstractItemView::setDirtyRegion(const QRegion &region)
{
   Q_D(QAbstractItemView);
   d->setDirtyRegion(region);
}

/*!
    Prepares the view for scrolling by (\a{dx},\a{dy}) pixels by moving the dirty regions in the
    opposite direction. You only need to call this function if you are implementing a scrolling
    viewport in your view subclass.

    If you implement scrollContentsBy() in a subclass of QAbstractItemView, call this function
    before you call QWidget::scroll() on the viewport. Alternatively, just call update().

    \sa scrollContentsBy(), dirtyRegionOffset(), setDirtyRegion()
*/
void QAbstractItemView::scrollDirtyRegion(int dx, int dy)
{
   Q_D(QAbstractItemView);
   d->scrollDirtyRegion(dx, dy);
}

/*!
    Returns the offset of the dirty regions in the view.

    If you use scrollDirtyRegion() and implement a paintEvent() in a subclass of
    QAbstractItemView, you should translate the area given by the paint event with
    the offset returned from this function.

    \sa scrollDirtyRegion(), setDirtyRegion()
*/
QPoint QAbstractItemView::dirtyRegionOffset() const
{
   Q_D(const QAbstractItemView);
   return d->scrollDelayOffset;
}

/*!
  \internal
*/
void QAbstractItemView::startAutoScroll()
{
   d_func()->startAutoScroll();
}

/*!
  \internal
*/
void QAbstractItemView::stopAutoScroll()
{
   d_func()->stopAutoScroll();
}

/*!
  \internal
*/
void QAbstractItemView::doAutoScroll()
{
   // find how much we should scroll with
   Q_D(QAbstractItemView);

   QScrollBar *verticalScroll = verticalScrollBar();
   QScrollBar *horizontalScroll = horizontalScrollBar();
   // QHeaderView does not (normally) have scrollbars
   // It needs to use its parents scroll instead
   QHeaderView *hv = qobject_cast<QHeaderView *>(this);
   if (hv) {
      QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget());
      if (parent) {
         if (hv->orientation() == Qt::Horizontal) {
            if (!hv->horizontalScrollBar() || !hv->horizontalScrollBar()->isVisible()) {
               horizontalScroll = parent->horizontalScrollBar();
            }
         } else {
            if (!hv->verticalScrollBar() || !hv->verticalScrollBar()->isVisible()) {
               verticalScroll = parent->verticalScrollBar();
            }
         }
      }
   }
   int verticalStep = verticalScroll->pageStep();
   int horizontalStep = horizontalScroll->pageStep();
   if (d->autoScrollCount < qMax(verticalStep, horizontalStep)) {
      ++d->autoScrollCount;
   }


   int margin = d->autoScrollMargin;
   int verticalValue = verticalScroll->value();
   int horizontalValue = horizontalScroll->value();

   QPoint pos = d->viewport->mapFromGlobal(QCursor::pos());
   QRect area = QWidgetPrivate::get(d->viewport)->clipRect();

   // do the scrolling if we are in the scroll margins
   if (pos.y() - area.top() < margin) {
      verticalScroll->setValue(verticalValue - d->autoScrollCount);
   } else if (area.bottom() - pos.y() < margin) {
      verticalScroll->setValue(verticalValue + d->autoScrollCount);
   }
   if (pos.x() - area.left() < margin) {
      horizontalScroll->setValue(horizontalValue - d->autoScrollCount);
   } else if (area.right() - pos.x() < margin) {
      horizontalScroll->setValue(horizontalValue + d->autoScrollCount);
   }

   // if nothing changed, stop scrolling
   bool verticalUnchanged = (verticalValue == verticalScroll->value());
   bool horizontalUnchanged = (horizontalValue == horizontalScroll->value());

   if (verticalUnchanged && horizontalUnchanged) {
      stopAutoScroll();
   } else {
#ifndef QT_NO_DRAGANDDROP
      d->dropIndicatorRect = QRect();
      d->dropIndicatorPosition = QAbstractItemView::OnViewport;
#endif
      d->viewport->update();
   }
}

/*!
    Returns the SelectionFlags to be used when updating a selection with
    to include the \a index specified. The \a event is a user input event,
    such as a mouse or keyboard event.

    Reimplement this function to define your own selection behavior.

    \sa setSelection()
*/
QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QModelIndex &index,
   const QEvent *event) const
{
   Q_D(const QAbstractItemView);
   Qt::KeyboardModifiers keyModifiers = Qt::NoModifier;
   if (event) {
      switch (event->type()) {
         case QEvent::MouseButtonDblClick:
         case QEvent::MouseButtonPress:
         case QEvent::MouseButtonRelease:
         case QEvent::MouseMove:
         case QEvent::KeyPress:
         case QEvent::KeyRelease:
            keyModifiers = (static_cast<const QInputEvent *>(event))->modifiers();
            break;
         default:
            keyModifiers = QApplication::keyboardModifiers();
      }
   }

   switch (d->selectionMode) {
      case NoSelection: // Never update selection model
         return QItemSelectionModel::NoUpdate;
      case SingleSelection: // ClearAndSelect on valid index otherwise NoUpdate
         if (event && event->type() == QEvent::MouseButtonRelease) {
            return QItemSelectionModel::NoUpdate;
         }
         if ((keyModifiers & Qt::ControlModifier) && d->selectionModel->isSelected(index)) {
            return QItemSelectionModel::Deselect | d->selectionBehaviorFlags();
         } else {
            return QItemSelectionModel::ClearAndSelect | d->selectionBehaviorFlags();
         }
      case MultiSelection:
         return d->multiSelectionCommand(index, event);
      case ExtendedSelection:
         return d->extendedSelectionCommand(index, event);
      case ContiguousSelection:
         return d->contiguousSelectionCommand(index, event);
   }

   return QItemSelectionModel::NoUpdate;
}

QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionCommand(
   const QModelIndex &index, const QEvent *event) const
{
   Q_UNUSED(index)

   if (event) {
      switch (event->type()) {
         case QEvent::KeyPress:
            if (static_cast<const QKeyEvent *>(event)->key() == Qt::Key_Space
               || static_cast<const QKeyEvent *>(event)->key() == Qt::Key_Select) {
               return QItemSelectionModel::Toggle | selectionBehaviorFlags();
            }
            break;
         case QEvent::MouseButtonPress:
            if (static_cast<const QMouseEvent *>(event)->button() == Qt::LeftButton) {
               return QItemSelectionModel::Toggle | selectionBehaviorFlags();   // toggle
            }
            break;
         case QEvent::MouseButtonRelease:
            if (static_cast<const QMouseEvent *>(event)->button() == Qt::LeftButton) {
               return QItemSelectionModel::NoUpdate | selectionBehaviorFlags();   // finalize
            }
            break;
         case QEvent::MouseMove:
            if (static_cast<const QMouseEvent *>(event)->buttons() & Qt::LeftButton) {
               return QItemSelectionModel::ToggleCurrent | selectionBehaviorFlags();   // toggle drag select
            }
         default:
            break;
      }
      return QItemSelectionModel::NoUpdate;
   }

   return QItemSelectionModel::Toggle | selectionBehaviorFlags();
}

QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::extendedSelectionCommand(
   const QModelIndex &index, const QEvent *event) const
{
   Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
   if (event) {
      switch (event->type()) {
         case QEvent::MouseMove: {
            // Toggle on MouseMove
            modifiers = static_cast<const QMouseEvent *>(event)->modifiers();
            if (modifiers & Qt::ControlModifier) {
               return QItemSelectionModel::ToggleCurrent | selectionBehaviorFlags();
            }
            break;
         }
         case QEvent::MouseButtonPress: {
            modifiers = static_cast<const QMouseEvent *>(event)->modifiers();
            const Qt::MouseButton button = static_cast<const QMouseEvent *>(event)->button();
            const bool rightButtonPressed = button & Qt::RightButton;
            const bool shiftKeyPressed = modifiers & Qt::ShiftModifier;
            const bool controlKeyPressed = modifiers & Qt::ControlModifier;
            const bool indexIsSelected = selectionModel->isSelected(index);
            if ((shiftKeyPressed || controlKeyPressed) && rightButtonPressed) {
               return QItemSelectionModel::NoUpdate;
            }
            if (!shiftKeyPressed && !controlKeyPressed && indexIsSelected) {
               return QItemSelectionModel::NoUpdate;
            }
            if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed) {
               return QItemSelectionModel::Clear;
            }
            if (!index.isValid()) {
               return QItemSelectionModel::NoUpdate;
            }
            break;
         }

         case QEvent::MouseButtonRelease: {
            // ClearAndSelect on MouseButtonRelease if MouseButtonPress on selected item or empty area
            modifiers = static_cast<const QMouseEvent *>(event)->modifiers();

            const Qt::MouseButton button  = static_cast<const QMouseEvent *>(event)->button();
            const bool rightButtonPressed = button & Qt::RightButton;
            const bool shiftKeyPressed    = modifiers & Qt::ShiftModifier;
            const bool controlKeyPressed  = modifiers & Qt::ControlModifier;

            if (((index == pressedIndex && selectionModel->isSelected(index))
                  || !index.isValid()) && state != QAbstractItemView::DragSelectingState
                  && !shiftKeyPressed && !controlKeyPressed && (!rightButtonPressed || !index.isValid())) {
               return QItemSelectionModel::ClearAndSelect | selectionBehaviorFlags();
            }

            return QItemSelectionModel::NoUpdate;
         }

         case QEvent::KeyPress: {
            // NoUpdate on Key movement and Ctrl
            modifiers = static_cast<const QKeyEvent *>(event)->modifiers();

            switch (static_cast<const QKeyEvent *>(event)->key()) {
               case Qt::Key_Backtab:
                  // special case for backtab
                  modifiers = modifiers & ~Qt::ShiftModifier;
                  [[fallthrough]];

               case Qt::Key_Down:
               case Qt::Key_Up:
               case Qt::Key_Left:
               case Qt::Key_Right:
               case Qt::Key_Home:
               case Qt::Key_End:
               case Qt::Key_PageUp:
               case Qt::Key_PageDown:

               case Qt::Key_Tab:

#ifdef QT_KEYPAD_NAVIGATION
                  if (modifiers & Qt::ControlModifier ||
                     QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder) {
                     // Preserve historical tab order navigation behavior
#else
                  if (modifiers & Qt::ControlModifier) {
#endif
                     return QItemSelectionModel::NoUpdate;
                  }

                  break;

               case Qt::Key_Select:
                  return QItemSelectionModel::Toggle | selectionBehaviorFlags();

               case Qt::Key_Space:// Toggle on Ctrl-Qt::Key_Space, Select on Space
                  if (modifiers & Qt::ControlModifier) {
                     return QItemSelectionModel::Toggle | selectionBehaviorFlags();
                  }

                  return QItemSelectionModel::Select | selectionBehaviorFlags();

               default:
                  break;
            }
         }
         default:
            break;
      }
   }

   if (modifiers & Qt::ShiftModifier) {
      return QItemSelectionModel::SelectCurrent | selectionBehaviorFlags();
   }
   if (modifiers & Qt::ControlModifier) {
      return QItemSelectionModel::Toggle | selectionBehaviorFlags();
   }
   if (state == QAbstractItemView::DragSelectingState) {
      //when drag-selecting we need to clear any previous selection and select the current one
      return QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent | selectionBehaviorFlags();
   }

   return QItemSelectionModel::ClearAndSelect | selectionBehaviorFlags();
}

QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::contiguousSelectionCommand(const QModelIndex &index,
   const QEvent *event) const
{
   QItemSelectionModel::SelectionFlags flags = extendedSelectionCommand(index, event);
   const int Mask = QItemSelectionModel::Clear | QItemSelectionModel::Select
      | QItemSelectionModel::Deselect | QItemSelectionModel::Toggle
      | QItemSelectionModel::Current;

   switch (flags & Mask) {
      case QItemSelectionModel::Clear:
      case QItemSelectionModel::ClearAndSelect:
      case QItemSelectionModel::SelectCurrent:
         return flags;
      case QItemSelectionModel::NoUpdate:
         if (event &&
            (event->type() == QEvent::MouseButtonPress
               || event->type() == QEvent::MouseButtonRelease)) {
            return flags;
         }
         return QItemSelectionModel::ClearAndSelect | selectionBehaviorFlags();
      default:
         return QItemSelectionModel::SelectCurrent | selectionBehaviorFlags();
   }
}

void QAbstractItemViewPrivate::fetchMore()
{
   fetchMoreTimer.stop();
   if (!model->canFetchMore(root)) {
      return;
   }
   int last = model->rowCount(root) - 1;
   if (last < 0) {
      model->fetchMore(root);
      return;
   }

   QModelIndex index = model->index(last, 0, root);
   QRect rect = q_func()->visualRect(index);
   if (viewport->rect().intersects(rect)) {
      model->fetchMore(root);
   }
}

bool QAbstractItemViewPrivate::shouldEdit(QAbstractItemView::EditTrigger trigger,
   const QModelIndex &index) const
{
   if (!index.isValid()) {
      return false;
   }
   Qt::ItemFlags flags = model->flags(index);
   if (((flags & Qt::ItemIsEditable) == 0) || ((flags & Qt::ItemIsEnabled) == 0)) {
      return false;
   }
   if (state == QAbstractItemView::EditingState) {
      return false;
   }
   if (hasEditor(index)) {
      return false;
   }
   if (trigger == QAbstractItemView::AllEditTriggers) { // force editing
      return true;
   }
   if ((trigger & editTriggers) == QAbstractItemView::SelectedClicked
      && !selectionModel->isSelected(index)) {
      return false;
   }
   return (trigger & editTriggers);
}

bool QAbstractItemViewPrivate::shouldForwardEvent(QAbstractItemView::EditTrigger trigger,
   const QEvent *event) const
{
   if (!event || (trigger & editTriggers) != QAbstractItemView::AnyKeyPressed) {
      return false;
   }

   switch (event->type()) {
      case QEvent::KeyPress:
      case QEvent::MouseButtonDblClick:
      case QEvent::MouseButtonPress:
      case QEvent::MouseButtonRelease:
      case QEvent::MouseMove:
         return true;
      default:
         break;
   };

   return false;
}

bool QAbstractItemViewPrivate::shouldAutoScroll(const QPoint &pos) const
{
   if (!autoScroll) {
      return false;
   }

   QRect area = static_cast<QAbstractItemView *>(viewport)->d_func()->clipRect();

   return (pos.y() - area.top() < autoScrollMargin)
      || (area.bottom() - pos.y() < autoScrollMargin)
      || (pos.x() - area.left() < autoScrollMargin)
      || (area.right() - pos.x() < autoScrollMargin);
}

void QAbstractItemViewPrivate::doDelayedItemsLayout(int delay)
{
   if (!delayedPendingLayout) {
      delayedPendingLayout = true;
      delayedLayout.start(delay, q_func());
   }
}

void QAbstractItemViewPrivate::interruptDelayedItemsLayout() const
{
   delayedLayout.stop();
   delayedPendingLayout = false;
}

void QAbstractItemViewPrivate::updateGeometry()
{
   Q_Q(QAbstractItemView);
   if (sizeAdjustPolicy == QAbstractScrollArea::AdjustIgnored) {
      return;
   }

   if (sizeAdjustPolicy == QAbstractScrollArea::AdjustToContents || !shownOnce) {
      q->updateGeometry();
   }
}

QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, const QStyleOptionViewItem &options)
{
   Q_Q(QAbstractItemView);

   QWidget *w = editorForIndex(index).widget.data();

   if (! w) {
      QAbstractItemDelegate *delegate = delegateForIndex(index);

      if (! delegate) {
         return nullptr;
      }

      w = delegate->createEditor(viewport, options, index);

      if (w) {
         w->installEventFilter(delegate);

         QObject::connect(w, &QWidget::destroyed, q, &QAbstractItemView::editorDestroyed);
         delegate->updateEditorGeometry(w, options, index);
         delegate->setEditorData(w, index);

         addEditor(index, w, false);
         if (w->parent() == viewport) {
            QWidget::setTabOrder(q, w);
         }

         // Special cases for some editors containing QLineEdit
         QWidget *focusWidget = w;
         while (QWidget *fp = focusWidget->focusProxy()) {
            focusWidget = fp;
         }

#ifndef QT_NO_LINEEDIT
         if (QLineEdit *le = qobject_cast<QLineEdit *>(focusWidget)) {
            le->selectAll();
         }
#endif

#ifndef QT_NO_SPINBOX
         if (QSpinBox *sb = qobject_cast<QSpinBox *>(focusWidget)) {
            sb->selectAll();
         } else if (QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox *>(focusWidget)) {
            dsb->selectAll();
         }
#endif
      }
   }

   return w;
}

void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QModelIndex &br)
{
   // counting on having relatively few editors
   const bool checkIndexes  = tl.isValid() && br.isValid();
   const QModelIndex parent = tl.parent();

   const QIndexEditorHash indexEditorHashCopy = indexEditorHash;
   QIndexEditorHash::const_iterator it = indexEditorHashCopy.constBegin();

   for (; it != indexEditorHashCopy.constEnd(); ++it) {
      QWidget *editor = it.value().widget.data();
      const QModelIndex index = it.key();

      if (it.value().isStatic || ! editor || ! index.isValid() ||
         (checkIndexes && (index.row() < tl.row() || index.row() > br.row() ||
               index.column() < tl.column() || index.column() > br.column() || index.parent() != parent))) {
         continue;
      }

      QAbstractItemDelegate *delegate = delegateForIndex(index);

      if (delegate) {
         delegate->setEditorData(editor, index);
      }
   }
}

/*!
    \internal

    In DND if something has been moved then this is called.
    Typically this means you should "remove" the selected item or row,
    but the behavior is view dependant (table just clears the selected indexes for example).

    Either remove the selected rows or clear them
*/
void QAbstractItemViewPrivate::clearOrRemove()
{
#ifndef QT_NO_DRAGANDDROP
   const QItemSelection selection = selectionModel->selection();
   QList<QItemSelectionRange>::const_iterator it = selection.constBegin();

   if (!overwrite) {
      for (; it != selection.constEnd(); ++it) {
         QModelIndex parent = (*it).parent();
         if ((*it).left() != 0) {
            continue;
         }
         if ((*it).right() != (model->columnCount(parent) - 1)) {
            continue;
         }
         int count = (*it).bottom() - (*it).top() + 1;
         model->removeRows((*it).top(), count, parent);
      }
   } else {
      // we can't remove the rows so reset the items (i.e. the view is like a table)
      QModelIndexList list = selection.indexes();

      for (int i = 0; i < list.size(); ++i) {
         QModelIndex index = list.at(i);
         QMap<int, QVariant> roles = model->itemData(index);

         for (QMap<int, QVariant>::iterator it = roles.begin(); it != roles.end(); ++it) {
            it.value() = QVariant();
         }
         model->setItemData(index, roles);
      }
   }
#endif
}

/*!
    \internal

    When persistent aeditor gets/loses focus, we need to check
    and setcorrectly the current index.
*/
void QAbstractItemViewPrivate::checkPersistentEditorFocus()
{
   Q_Q(QAbstractItemView);
   if (QWidget *widget = QApplication::focusWidget()) {
      if (persistent.contains(widget)) {
         //a persistent editor has gained the focus
         QModelIndex index = indexForEditor(widget);
         if (selectionModel->currentIndex() != index) {
            q->setCurrentIndex(index);
         }
      }
   }
}


const QEditorInfo &QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const
{
   static QEditorInfo nullInfo;

   // do not try to search to avoid slow implicit cast from QModelIndex to QPersistentModelIndex
   if (indexEditorHash.isEmpty()) {
      return nullInfo;
   }

   QIndexEditorHash::const_iterator it = indexEditorHash.find(index);
   if (it == indexEditorHash.end()) {
      return nullInfo;
   }

   return it.value();
}

bool QAbstractItemViewPrivate::hasEditor(const QModelIndex &index) const
{
   return indexEditorHash.find(index) != indexEditorHash.constEnd();
}

QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const
{
   // do not try to search to avoid slow implicit cast from QModelIndex to QPersistentModelIndex
   if (indexEditorHash.isEmpty()) {
      return QModelIndex();
   }

   QEditorIndexHash::const_iterator it = editorIndexHash.find(editor);
   if (it == editorIndexHash.end()) {
      return QModelIndex();
   }

   return it.value();
}

void QAbstractItemViewPrivate::removeEditor(QWidget *editor)
{
   QEditorIndexHash::iterator it = editorIndexHash.find(editor);
   if (it != editorIndexHash.end()) {
      indexEditorHash.remove(it.value());
      editorIndexHash.erase(it);
   }
}

void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic)
{
   editorIndexHash.insert(editor, index);
   indexEditorHash.insert(index, QEditorInfo(editor, isStatic));
}

bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const
{
   Q_Q(const QAbstractItemView);

   QModelIndex buddy = model->buddy(index);
   QStyleOptionViewItem options = viewOptions();

   options.rect = q->visualRect(buddy);
   options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);

   QAbstractItemDelegate *delegate = delegateForIndex(index);
   return (event && delegate && delegate->editorEvent(event, model, options, buddy));
}

bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *event)
{
   Q_Q(QAbstractItemView);

   QModelIndex buddy = model->buddy(index);
   QStyleOptionViewItem options = viewOptions();

   options.rect = q->visualRect(buddy);
   options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);

   QWidget *w = editor(buddy, options);
   if (! w)  {
      return false;
   }

   q->setState(QAbstractItemView::EditingState);
   w->show();
   w->setFocus();

   if (event)  {
      QApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event);
   }

   return true;
}

QItemViewPaintPairs QAbstractItemViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
{
   Q_ASSERT(r);
   Q_Q(const QAbstractItemView);
   QRect &rect = *r;
   const QRect viewportRect = viewport->rect();
   QItemViewPaintPairs ret;
   for (int i = 0; i < indexes.count(); ++i) {
      const QModelIndex &index = indexes.at(i);
      const QRect current = q->visualRect(index);
      if (current.intersects(viewportRect)) {
         ret += qMakePair(current, index);
         rect |= current;
      }
   }
   rect &= viewportRect;
   return ret;
}

QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const
{
   Q_ASSERT(r);
   QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r);
   if (paintPairs.isEmpty()) {
      return QPixmap();
   }

   qreal scale = 1.0f;

   Q_Q(const QAbstractItemView);
   QWidget *window = q->window();
   if (window) {
      QWindow *windowHandle = window->windowHandle();
      if (windowHandle) {
         scale = windowHandle->devicePixelRatio();
      }
   }

   QPixmap pixmap(r->size() * scale);
   pixmap.setDevicePixelRatio(scale);
   pixmap.fill(Qt::transparent);
   QPainter painter(&pixmap);
   QStyleOptionViewItem option = viewOptions();
   option.state |= QStyle::State_Selected;
   for (int j = 0; j < paintPairs.count(); ++j) {
      option.rect = paintPairs.at(j).first.translated(-r->topLeft());
      const QModelIndex &current = paintPairs.at(j).second;
      adjustViewOptionsForIndex(&option, current);
      delegateForIndex(current)->paint(&painter, option, current);
   }
   return pixmap;
}

void QAbstractItemViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command)
{
   if (!selectionModel) {
      return;
   }

   QItemSelection selection;
   QModelIndex tl = model->index(0, 0, root);
   QModelIndex br = model->index(model->rowCount(root) - 1,
         model->columnCount(root) - 1,
         root);
   selection.append(QItemSelectionRange(tl, br));
   selectionModel->select(selection, command);
}

QModelIndexList QAbstractItemViewPrivate::selectedDraggableIndexes() const
{
   Q_Q(const QAbstractItemView);
   QModelIndexList indexes = q->selectedIndexes();
   for (int i = indexes.count() - 1 ; i >= 0; --i) {
      if (!isIndexDragEnabled(indexes.at(i))) {
         indexes.removeAt(i);
      }
   }
   return indexes;
}

void QAbstractItemView::_q_columnsAboutToBeRemoved(const QModelIndex &un_named_arg1, int un_named_arg2,
   int un_named_arg3)
{
   Q_D(QAbstractItemView);
   d->_q_columnsAboutToBeRemoved(un_named_arg1, un_named_arg2, un_named_arg3);
}

void QAbstractItemView::_q_columnsRemoved(const QModelIndex &un_named_arg1, int un_named_arg2, int un_named_arg3)
{
   Q_D(QAbstractItemView);
   d->_q_columnsRemoved(un_named_arg1, un_named_arg2, un_named_arg3);
}

void QAbstractItemView::_q_columnsInserted(const QModelIndex &un_named_arg1, int un_named_arg2, int un_named_arg3)
{
   Q_D(QAbstractItemView);
   d->_q_columnsInserted(un_named_arg1, un_named_arg2, un_named_arg3);
}

void QAbstractItemView::_q_columnsMoved(const QModelIndex &un_named_arg1, int un_named_arg2, int un_named_arg3,
                  const QModelIndex &un_named_arg4, int un_named_arg5)
{
   Q_D(QAbstractItemView);
   d->_q_columnsMoved(un_named_arg1, un_named_arg2, un_named_arg3, un_named_arg4, un_named_arg5);
}

void QAbstractItemView::_q_headerDataChanged()
{
   Q_D(QAbstractItemView);
   d->_q_headerDataChanged();
}

void QAbstractItemView::_q_rowsInserted(const QModelIndex &un_named_arg1, int un_named_arg2, int un_named_arg3)
{
   Q_D(QAbstractItemView);
   d->_q_rowsInserted(un_named_arg1, un_named_arg2, un_named_arg3);
}

void QAbstractItemView::_q_rowsRemoved(const QModelIndex &un_named_arg1, int un_named_arg2, int un_named_arg3)
{
   Q_D(QAbstractItemView);
   d->_q_rowsRemoved(un_named_arg1, un_named_arg2, un_named_arg3);
}

void QAbstractItemView::_q_rowsMoved(const QModelIndex &un_named_arg1, int un_named_arg2, int un_named_arg3,
                  const QModelIndex &un_named_arg4, int un_named_arg5)
{
   Q_D(QAbstractItemView);
   d->_q_rowsMoved(un_named_arg1, un_named_arg2, un_named_arg3, un_named_arg4, un_named_arg5);
}

void QAbstractItemView::_q_modelDestroyed()
{
   Q_D(QAbstractItemView);
   d->_q_modelDestroyed();
}

void QAbstractItemView::_q_layoutChanged()
{
   Q_D(QAbstractItemView);
   d->_q_layoutChanged();
}

void QAbstractItemView::_q_scrollerStateChanged()
{
   Q_D(QAbstractItemView);
   d->_q_scrollerStateChanged();
}

#endif // QT_NO_ITEMVIEWS
